chongo@nsc.UUCP (Landon Noll) (04/12/85)
In net.sources: I am reposting DBell's DPY sources in responce to all the folks who needed them for his LIFE and WAR progs. DBell is no longer on the net (in fact in a few days he will no longer be in this hemisphere) otherwise he would have done this himself. In net.sources.games: I am reposting DBell's LIFE sources complete with the changes for the new DPY. Both LIFE and WAR (which was posted by DBell not long ago) require the use of DPY so be sure and GRAB IT FROM net.sources NOW!!! ---------------------begin DBell's comments------------------------------- This article (and the next 2) contain sources to my version of a program which plays the mathematical game of life. Its features include: * An 'infinite' sized board * Display routines capable of showing large objects * Editing of multiple life objects at once * Provides macros, loops, and variables * Allows rule changing to create alternate life universes * Supports libraries of life objects * Friendly storage format for objects This program requires my 'dpy' module for screen handling. That module was posted very recently by me. If necessary, I can mail copies to you. Part 4 of this posting contains a library of life objects which I have seen or created, along with comments for each one. I am very interested in receiving other interesting life objects to add to the library. Thanks!! - nsc!daisy!dbell - #---Cut here and place in it's own directory, then feed to Bourne shell--- # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # This archive contains: # README (1117 chars) # life.doc (21688 chars) # makefile (357 chars) # life.h (7899 chars) # echo x - README sed -e 's/^X//' > "README" << '//E*O*F README//' XThis is a version of life which was written to run under 4.2 BSD UNIX Xrunning on a VAX 750. If you are not running this configuration, you Xwill probably have to make modifications. The only Berkeley specific Xfeature used (that I can recall) is the use of the FIONREAD ioctl. But Xthere are many int/long mismatches (some of us on VAXes are rather lax). X XThis program requires the use of my "dpy" library module for its screen Xupdating. This module was recently posted to net.sources. If you did Xnot receive that module, I can send it to you privately if necessary. XChanging the life program to use curses would be possible, but painful. X XA program like this can have almost an infinite number of features. XThere are still things I would like to include in it, but enough major Xthings are implemented to make it very useful. Suggestions for new Xfeatures or changes are welcome, as are the (to be expected) bug fixes. X XOnce again, sorry about the the documentation. Life.doc should be formatted, Xbut the life program would probably have waited forever until I did that. XEnjoy! X David I. Bell X nsc!daisy!dbell //E*O*F README// echo x - life.doc sed -e 's/^X//' > "life.doc" << '//E*O*F life.doc//' X The Game of LIFE X program by David I. Bell X X XIf you do not know what the game of life is, see the appendix at the end. XExcept in that appendix, the remainder of this document assumes that you Xknow the rules and terms of the game. X XThis program plays the game of life on a practically infinite board by using Xsparse matrices. Therefore there is no hard limit on the size of a life Xobject except for its memory requirements. The memory requirements depend Xon the number of live cells of the object, and not on their locality. The Xtime needed to compute one generation also depends on the number of live cells. XThe speed is such that on a VAX 750, an object with 2000 live cells takes Xabout 1 second of run time per generation. Typical objects run much faster. X XLife programs are limited by the size of their display. On special kinds of Xterminals, very large objects can be seen directly. However, this program Xis designed to run on the vast majority of terminals, most of which have Xdisplays which are limited to showing 24 by 80 characters. The program gets Xaround this limitation by doing two things. Firstly, the standard display Xcan show any portion of an object. As the cursor is moved around the object, Xthe view scrolls as necessary in any direction so that the location of the Xcursor remains visible. Thus you can move to any point in the object and Xsee what is nearby. Secondly, the display can be scaled by any amount up Xto 1000. This means that each position on the screen can represent an area Xwhich is up to 1000 by 1000 cells of the object. Obviously, information must Xbe lost when this is done. So the program displays the number of live cells Xin each region, from 1 to 9 (or * for 10 or more). In practice this display Xis perfectly adequate for seeing what is happening. Once you have moved the Xcursor to an interesting location, you can reduce the scale to see what is Xactually there in detail. The 's' command sets the viewing scale as desired, Xand 'S' sets auto-scale mode, which keeps an object visible as it grows. XBoth commands recenter the view around the current cursor position. When Xyou are trying to count the number of cells between two parts of an object, Xthe ';grid' command is useful. It changes the display of dead cells from Xblanks to another character (typically periods). X XThis program can simulaneously handle more than one object. Each object is Xdistinct and has its own name, and you can move between objects to select the Xone currently being edited. The ';create name' command creates a new object, Xand the ';edit name' switches between objects (defaulting to the previously Xedited object if no name is given). Cells can be copied or moved between Xobjects. This allows you to make backups of an object, compare two objects, Xor split up and edit the parts of a single object. The ';copy' command Xcopies the current object to another object. The ';insert' command inserts Xanother object into the current object. This is done in such as way that the Xtwo object's cursor locations match. The ';list' command will display data Xabout all objects whose names do not begin with a period (adding '-a' to the Xcommand will show even those). Objects with names beginning with '..' are Xreserved, and have special uses like temporary object manipulation, the saving Xof deleted objects, and for implementing undo. X XMost commands (and the viewpoint), are based on the current cursor location. XTo change the viewpoint, or to point at a location in the object that you Xwish to modify, you must move the cursor around in the object. To do this, Xyou can use some single character commands. The directions for movement Xare like in rogue, so that for example, 'h' moves left, 'j' moves down, 'y' Xmoves to the upper left, and so on. The commands are multiplied by the scale Xfactor, so that you can move about in large objects easily. In addition, Xthey accept an optional preceeding numeric argument, which multiplies their Xmovement by the given amount. Also, space (or period) moves to the right, Xwhile return moves down to the next line and back to the column of the pointer. XThe pointer is simply a location that is remembered in an object, and only a Xfew commands use it. The '@' command sets the pointer to the cursor location. XThe movement commands when capitalized perform a shift of both the cursor Xand the viewing region together by 1/4 of the screen size. This is useful Xto shift the view slightly to see an object on the edge better. X XThe simplest commands for changing an object are 'o', 'x', 't', and 'i'. XThe 'o' command creates a live cell and moves the cursor to the right. XThe 'x' command creates a dead cell and moves the cursor to the right. XBoth of these commands can accept a numeric argument which replicates their Xaction by the given amount (as an example, the command string '25o' creates Xa row of 25 live cells). The 't' command toggles the current cell from Xdead to alive, or from alive to dead, without moving the cursor. The 'i' Xcommand sets a mode in which some of the movement commands described in Xthe previous paragraph automatically insert or delete cells. This makes it Xeasy to create lines of cells in any direction. X XA very useful feature is the use of 'selections'. You can select any group Xof cells of an object, and handle them specially, leaving the rest of the Xcells unchanged. For example, you can save the selected cells as a different Xobject, or can move them around, or can delete them. Selected cells appear Xdifferently in the display (as hash marks), so that you can recognize which Xcells are selected. Selections are set based on where the cursor is. You Xcan select all the cells in any quadrant or half-plane around the cursor. XYou can also select all the cells which are king-wise connected to your cursor Xlocation. Each selection command adds to the set of selected cells. The 'M' Xcommand removes all current selections so you can start a new set. There are Xseveral commands which act on a selection in interesting ways. For example, Xif the cursor is on a glider, the command 'dd' will delete just the glider. XThe commands which flip or rotate selections do so with respect to the current Xcursor location. X XTo compute generations, you use the 'g' or 'G' commands. The 'g' command Xcomputes a single generation of the current object, or as many generations Xas you specified in its preceeding argument. The 'G' command simply means Xrun for an infinite number of generations. If the object dies or becomes Xstable, computations automatically stop. However, loops of period 2 or Xgreater are not detected. While generations are being computed, you can use Xthe movement or scaling commands to wander around the object. But attempts Xto change the object while it is running will be rejected. If you wish to Xwait until the indicated computations are complete before the next command is Xexecuted, use the ';wait' command (this is often used inside of a command Xmacro or loop). To stop the computations for an object, use the 'g' command Xagain to reset the count to zero. Normally each generation will be displayed. XTo speed up the computations when you don't want to see every generation, use Xthe ';frequency n' command to set how often the screen is updated. X XThis program can read commands from a file. It can also save objects out to Xa file. When writing an object to a file, what is actually written is a Xsequence of commands which regenerates the object. The choice of commands is Xsuch that for small objects, the commands look like a picture of the object. XThus, the object is written using the commands '.' (to move right), 'o' (to Xinsert cells), and '\n' (to move to the next line). (Periods are used instead Xof spaces so that you can count the spaces between live cells easily. However, Xspaces or tabs could also be used and will act reasonably.) Thus for small Xobjects, you can create a picture of an object in an editor, and then the Xobject can be read in by the life program. Lines beginning with '!' or '#' Xare ignored, so you can comment your objects. When writing out an object, if Xit is too large to be typed to your screen or edited easily, the output will Xcontain numeric arguments as necessary in order to compress the resulting file. XThe command ';write filename' is used to save an object. X XWhen reading an input file, the program looks for six different file names in Xsequence. It starts by looking for the file name as given. Then it looks for Xthe name with an '.l' extension appended. Then, if the environment variable XLIFELIB is defined, it looks in that directory for the file, and then for the Xfile with the '.l' extension. Finally, it looks in /usr/games/lib/life for Xthe file, and then for the file with the '.l' extension. Thus there is a Xstandard library of interesting life objects, and you can create your own Xlife library. The ';read filename' command reads back a command file. In Xaddition, when the life program is started, you can specify a filename on the Xcommand line which will be read before any commands are read from the terminal. XThis is useful when you want to immediately read a life object from a library. X XCommand loops and macros can be defined using the '<' and '>' commands. If Xa numeric argument is specified, a loop is being defined. If no argument is Xgiven, a macro is being defined. While defining a macro or loop, the commands Xyou type are executed normally, so that you can see how the definition will Xexecute. This is useful when you define a macro to create an object, since Xyou can easily see how to create the object. Loops and macros can contain Xboth line mode and character mode style commands. While you are defining a Xloop or macro, the status line will indicate this and give the current depth Xof loop or macro nesting. X XWhen defining a macro, the next letter after the '<' is the macro name being Xdefined (the name must be a lower case letter). All of the commands between Xthe '<' and the '>' (except for the macro name character) are saved. Once a Xmacro has been defined, it is used by typing the ESCAPE character followed by Xthe macro letter. As an example, the command string '<gththtuktntj>' defines Xa macro named 'g' which causes a glider to be inserted at the current cursor Xposition every time you type '<ESC>g'. The ';dumpmacros file' command writes Xout the definitions of all macros so that you can read them back in later. X XIf a loop is being defined, the loop is executed the number of times specifed Xby the argument to '<'. If two arguments are specifed, the loop runs from Xthe first to the second argument (backwards if the second argument is less Xthan the first argument). The value of the loop counter is available in the Xloop itself by using '%' as you would a number. As an example of a loop, Xthe command string '3,7<%o >' inserts five strings of cells spaced apart by Xone cell, where the first string contains three cells, and the last string Xcontains seven cells. When nesting loops, supplying an argument to '%' can Xobtain the loop counter values for the outside loops while in the inner loop. XThus, '2%' obtains the loop value for the next-to-innermost loop level. X XWhile defining a loop or macro, you can request user intervention by using Xthe ';ttyinput' command. This will suspend the execution of the loop or macro Xand notify the user that input is desired by displaying 'tty-input' in the Xstatus line. The user can then execute any commands of his choice. When he Xis ready to proceed, he uses the ';endinput' (or a control-D) command to Xcontinue execution where it had paused. Different commands can be given by Xthe user each time the loop or macro asks for terminal input. If the command X';ttyinput -c' is used, terminal commands will only be asked for if there Xis any terminal input available to be read. X XWhile a macro or loop is executing, there is no screen updating. Thus a Xcomplicated command sequence can be executed without letting the user see Xwhat is occuring until it is finished. However, if you wish to update the Xscreen in the middle of execution, the ';updateview' command will do this. XCommands from the terminal automatically do an update whenever no more Xinput is available. X XThere is a set of variables which you can use to influence your commands. XThey come in two kinds. The first kind is the single-character variable. XThere are 52 of these variables (the lower and upper case letters). These Xvariables are used with a leading dollar sign. Any command which accepts an Xargument can accept '$c' to mean the value of variable 'c'. These variables Xcan be set using the ';set' command, which takes a variable name followed by Xan expression. (As a shortcut, the '+c' command increments variable c.) XThe initial values of all variables are zero. These variables are useful Xinside a macro in order to execute a command string over and over again with Xonly small differences each time (such as to vary the placement of two objects Xwith respect to each other). As an example of the use of a single-character Xvariable, the command string '$al' moves the cursor to the right by the number Xof cells given by the value of variable 'a'. X XThe other kind of variables are the multi-character variables. These are Xa fixed set of names, and their values are specific things, and cannot be Xchanged. For example, the 'cx' variable returns the x coordinate of the Xcurrent cursor position. You can see the complete set of variables using Xthe ';variables' command. Since the parser must be able to distinguish Xvariable names from command letters, you cannot use them without surrounding Xthem with a pair of parenthesis. The parenthesis can actually contain any Xexpression, including single-character variables. When inside parenthesis Xor as part of a line mode command, variables do not need the leading '$'. XAs an example of multi-character variables used with an expression, the Xcommand string '((vmaxx-vminx)/2)l' shifts the cursor to the right by an Xamount equal to half of the screen width. X XThe rules of life can be changed to some degree. This allows you to explore Xalternative life universes. You can specify how many live cells are required Xfor a cell to be born, or to stay alive. However, you cannot change the Xneighborhood used for counting live cells (the program always counts all of Xthe eight neighboring cells). To change the cells needed for birth or death, Xuse the ';rules <born> <live>' line mode command. The <born> string lists Xthose counts of neighbors which are required for a new cell to be born. The X<live> string lists those counts of neighbors which are required for an Xexisting cell to stay alive. The standard rules are thus described by the Xcommand ';rules 3 23'. X XThere are two kinds of commands to life. Line mode commands begin with a Xcolon or semi-colon character, and are terminated by an end of line character. XMany of these have already been described above. Use the ';help' command to Xlist all of the line mode commands. They are reasonably self-documenting. XHere are some of the remaining useful line mode commands. The ';quit' command Xexits the program. The ';zero' command clears all cells of an object and Xresets the scaling factor and cursor position. The ';copyselection name' and X';moveselection name' commands copy or move selected cells to another object. XFinally, the ';undo' command will undo the most recent change to the current Xobject. This can undo deletions, insertions, and running of generations, Xamong other things. But you get only one level of backup (A related backup Xfeature is the 'p' command, which will reinsert the last deleted object to Xthe current cursor location.) X XCharacter mode commands are executed immediately when complete. Until it is Xcomplete, it can be edited using the normal erase and kill characters. The Xcharacter mode commands can be preceeded by zero, one, or two arguments. XThese arguments are generally used as a count of how many times the command Xis to be executed. Missing arguments are defaulted to 1 (with the exceptions Xnoted in the tables below). If two arguments are specified, they are separated Xby a comma. So a character mode command can take one of the following forms: X X <cmd> Both arguments defaulted. X <arg1> <cmd> First argument given, second is defaulted. X<arg1> , <arg2> <cmd> Both arguments given. X XThe following table specifies the character mode commands, arranged in Xuseful catagories. Many of these have been described in detail above. X X X --- MOVEMENT COMMANDS --- X XSPACE move right <arg1> cells X. move right <arg1> cells Xh move left <arg1> cells Xl move right <arg1> cells Xk move up <arg1> cells Xj move down <arg1> cells Xy move upper left <arg1> cells Xu move upper right <arg1> cells Xb move lower left <arg1> cells Xn move lower right <arg1> cells XLF move to next "line" XTAB move to next "tab stop" X@ remember current location as the pointer Xc return to location of the pointer X/ move cursor to next object X X X --- SCREEN COMMANDS --- X Xs set viewing scale to <arg1> and center view (default current scale) XS turn on auto-scaling and center view XH shift view left by width/4 XL shift view right by width/4 XK shift view up by height/4 XJ shift view down by height/4 XY shift view left and up XU shift view right and up XB shift view left and down XN shift view right and down X^L refresh the screen X X X --- SINGLE CELL COMMANDS -- X Xo insert <arg1> cells and move cursor right XO insert <arg1> cells and move cursor right Xx kill <arg1> cells and move cursor right Xt toggle cell at current location Xi toggle movement mode to move, insert, or delete X X X --- MULTIPLE CELL COMMANDS --- X Xd<sel> delete cells described by <sel> Xp place last deleted object at current location Xf<sel> flip cells left-to-right as described by <sel> Xr<sel> rotate cells 90 degrees clockwise as described by <sel> Xm<sel> mark (select) cells as described by <sel> XM remove all marks (selections) X X X --- LOOP, VARIABLE, AND MACRO COMMANDS --- X X< begin loop which executes from <arg1> to <arg2> times (if <arg1> given) X<<ch> begin definition of macro named <ch> (if <arg1> not given) X> end loop or macro definition XESC<ch> execute a macro command named <ch> X+<ch> increment the value of the single-character variable <ch> by arg1 X X X --- MISCELLANEOUS COMMANDS --- X Xg compute <arg1> generations (or stop if running) XG compute infinite generations Xz set generation number to <arg1> (default zero) X! ignore characters until end of line X# ignore character until end of line X:<line> execute a line mode command X;<line> execute a line mode command X X XArguments specified above as <arg1> and <arg2> can be any of the following: X$<var> value of variable <var>, where <var> is a lower or upper case letter X(expr) an arithmetic expression containing constants, variables, or operators X% current loop counter value X<num> explicit numeric value X X XThose commands above referencing <sel> take one of the following letters. XThe command affects only those cells selected. As a special case, repeating Xthe command letter is equivilant to selecting 'o' (the connected object). XExample: 'dd' deletes the connected object the cursor is on. Xa all cells Xc cell at current location Xo cells in the king-wise connected object at current location Xh cells to left of cursor Xl cells to right of cursor Xk cells above cursor Xj cells below cursor Xb cells below and left of cursor Xn cells below and right of cursor Xu cells above and right of cursor Xy cells above and left of cursor Xp cells in rectangle determined by pointer and cursor Xm cells which are marked Xv cells visible in window X X X APPENDIX X XThis is a short introduction to life. Life is played on an infinitely large Xboard divided into squares. Each square is called a cell. Each cell can be Xeither dead or alive. Dead cells are seen as blanks, whereas live cells are Xseen as non-blanks. You begin to play by choosing some arbitrary set of Xlive and dead cells. This configuration is called generation 0. There is Xa set of rules which transforms this set of cells into another set of cells, Xcalled generation 1. These same rules are then reapplied to generation 1 to Xproduce generation 2. This process continues indefinitely. The 'purpose' of Xthe game is to find starting patterns such that 'interesting things' result. X XThe rules which are applied are as follows. Take any cell of a generation, Xand call it the current cell. Consider the eight cells immediately adjacent Xto the current cell. Count the number of these eight cells which are alive. XIf the current cell is dead and the count is 3, then the current cell changes Xto a live cell in the next generation. If the current cell is alive and the Xcount is NOT 2 or 3, then the current cell changes to a dead cell in the next Xgeneration. Otherwise the cell remains unchanged in the next generation. XThis rule is applied to every cell of a generation SIMULTANEOUSLY. Thus to Xrephrase, 3 live neighbors causes a new cell to be born, whereas 2 or 3 live Xneighbors keeps a cell alive. X XTo see how these rules work in practice, run the program and start with some Xnumber of live cells in some arrangement, and watch the generations change. XIf you can predict what the changes will be, then you understand the rules. XThe following are some objects to try, along with some descriptive names. XWarning: the last example gets complicated!! X X O O O O O OO X O O OO OO OO O OO X O O O O O OOO O X Xdoomed blinker beehive block traffic lights glider r-pentomino //E*O*F life.doc// echo x - makefile sed -e 's/^X//' > "makefile" << '//E*O*F makefile//' X# @(#)makefile 1.3 1/31/85 X# @(#) X XCFLAGS = -O XCC = cc X XCFILES = alloc.c cell.c cmd.c cmdl.c debug.c gen.c io.c main.c mark.c\ X object.c scan.c vars.c view.c X XOFILES = alloc.o cell.o cmd.o cmdl.o debug.o gen.o io.o main.o mark.o\ X object.o scan.o vars.o view.o X Xlife: ${OFILES} makefile X ${CC} ${CFLAGS} -o life ${OFILES} -ldpy -ltermlib X X${OFILES}: life.h //E*O*F makefile// echo x - life.h sed -e 's/^X//' > "life.h" << '//E*O*F life.h//' X/* X * @(#)life.h 1.9 1/31/85 X * @(#)Copyright (C) 1985 by D Bell X */ X X#include <stdio.h> X#include <signal.h> X#include <setjmp.h> X X#define LIFELIB "/usr/games/lib/life" /* directory for general life library */ X#define LIFEVAR "LIFELIB" /* environment name for user's lib */ X#define ALLOCOBJ 10 /* how many new objects to allocate */ X#define ALLOCROW 50 /* how many new rows to allocate */ X#define ALLOCCELL 500 /* how many new cells to allocate */ X#define FILESIZE (1024*4) /* file buffer size */ X#define LOOPSIZE 5000 /* characters in command loops */ X#define SCAN_SIZE 100 /* maximum command length */ X#define MAXINPUT 20 /* maximum nesting of command inputs */ X#define MAXNAME 32 /* maximum object name size */ X#define MAXSCALE 1000 /* maximum scale factor */ X#define WRITEROWS 100 /* default maximum rows for writing */ X#define WRITECOLS 79 /* default maximum cols for writing */ X#define ESC '\033' /* escape character */ X#define INFINITY 0x7fffffff /* infinite value */ X#define LIFE 9 /* live cell value in neighbor count */ X#define STDIN 0 /* standard input */ X#define STDOUT 1 /* standard output */ X#define STDERR 2 /* standard error */ X#define RELATIVE 0 /* do object additions relatively */ X#define ABSOLUTE 1 /* do object additions absolutely */ X#define M_MOVE 0 /* movement mode */ X#define M_INSERT 1 /* insertion mode */ X#define M_DELETE 2 /* deletion mode */ X#define MARK_ANY 0x1 /* mark always set */ X#define MARK_USR 0x2 /* marked due to user specification */ X#define MARK_CMD 0x4 /* marked only for current command */ X#define MARK_SRC 0x8 /* marked by searching */ X#define MARK_SEE (MARK_USR) /* marks seen by user */ X#define MARK_ALL (MARK_ANY|MARK_USR|MARK_CMD|MARK_SRC) /* all non-null marks */ X#define INP_TTY 0 /* input is terminal */ X#define INP_FILE 1 /* input is file */ X#define INP_LOOP 2 /* input is command loop */ X#define INP_MACRO 3 /* input is command macro */ X#define SCAN_ABORT 1 /* setjmp value for aborted command */ X#define SCAN_EDIT 2 /* setjmp value for edited command */ X#define SCAN_EOF 3 /* setjmp value for no command data */ X#define crow curobj->o_currow /* current row of current object */ X#define ccol curobj->o_curcol /* current column of current object */ X#define cmark curobj->o_mark /* current mark being applied */ X#define cscale curobj->o_scale /* current scale of current object */ X#define prow curobj->o_prow /* current pointer row */ X#define pcol curobj->o_pcol /* current pointer column */ X X/* macro to detect reserved names */ X#define BADNAME(s) ((s[0] == '.') && ((s[1] == '\0') || (s[1] == '.'))) X X Xstruct object { /* structure for each object */ X struct row *o_firstrow; /* first row */ X struct row *o_lastrow; /* last row */ X struct object *o_next; /* next object */ X long o_count; /* number of live cells */ X long o_born; /* number of cells born */ X long o_died; /* number of cells died */ X long o_currow; /* current row */ X long o_curcol; /* current column */ X long o_minrow; /* minimum row seen in window */ X long o_maxrow; /* maximum row seen in window */ X long o_mincol; /* minimum column seen in window */ X long o_maxcol; /* maximum column seen in window */ X long o_prow; /* currently pointed at row */ X long o_pcol; /* currently pointed at column */ X long o_gen; /* current generation */ X short o_scale; /* current scaling factor for view */ X char o_autoscale; /* doing autoscaling */ X char o_mark; /* mark value for new cells */ X char o_reserved; /* reserved object */ X char o_lock; /* locked against computing gens */ X char o_name[MAXNAME+1]; /* name of object */ X}; X Xstruct row { /* structure for each row of cells */ X struct row *r_next; /* link to next row */ X struct cell *r_firstcell; /* link to first cell */ X struct cell *r_lastcell; /* link to last real cell */ X long r_row; /* row number */ X long r_count; /* number of cells in this row */ X}; X Xstruct cell { /* structure for each cell */ X struct cell *c_next; /* link to next cell */ X long c_col; /* column number */ X int c_marks; /* marking values for cell */ X}; X X X/* X * The following structure holds all data necessary for processing X * characters from some source. X */ Xstruct input { X int (*i_getchar)(); /* routine to read next character */ X int (*i_term)(); /* routine to terminate reading */ X char *i_begptr; /* beginning of command data */ X char *i_endptr; /* end of command data */ X char *i_curptr; /* current character */ X char i_type; /* type of input */ X /* following data for file reading only */ X short i_file; /* file descriptor */ X struct object *i_obj; /* object to restore on reentry */ X long i_row; /* row to restore */ X long i_col; /* column to restore */ X long i_prow; /* pointer row to restore */ X long i_pcol; /* pointer column to restore */ X /* following data for loop or macro reading only */ X long i_curval; /* current iteration value */ X long i_endval; /* ending iteration value */ X int i_first; /* processing new chars */ X char i_macro; /* macro being defined */ X}; X X Xstruct macro { /* structure for command macros */ X char *m_begptr; /* beginning of data (NULL if none) */ X char *m_endptr; /* end of data */ X}; X X Xstruct object *objects; /* list of active objects */ Xstruct object *curobj; /* currently selected object */ Xstruct object *prevobj; /* previously selected object */ Xstruct object *mainobject; /* the main object */ Xstruct object *deleteobject; /* object last deleted */ Xstruct object *backupobject; /* backup object */ Xstruct object *tempobject; /* temporary object */ Xstruct object *freeobjects; /* list of free objects */ Xstruct object *newobjects; /* top of new object allocation */ Xstruct object *endobjects; /* end of new objects */ X Xstruct row *freerows; /* list of free row structures */ Xstruct row *newrows; /* top of new row allocation */ Xstruct row *endrows; /* end of new rows */ Xstruct row *termrow; /* terminus row */ Xstruct row initrow; /* row to initialize list */ X Xstruct cell *freecells; /* list of free cell structures */ Xstruct cell *newcells; /* top of new cell allocation */ Xstruct cell *endcells; /* end of new cells */ Xstruct cell *termcell; /* terminus cell */ Xstruct cell initcell; /* cell to initialize list */ X Xstruct input *curinput; /* current input being read from */ X Xint seecount; /* number of cells we can see */ Xint frequency; /* typeout frequency */ Xint freqcount; /* current count */ Xint genleft; /* generations left before stopping */ Xshort rowradius; /* half of length of screen */ Xshort colradius; /* half of width of screen */ Xchar reserve; /* reserved object names allowed */ Xchar dowait; /* must wait for input */ Xchar update; /* status or position needs updating */ Xchar redraw; /* whole screen needs updating */ Xchar interact; /* still being interactive */ Xchar stop; /* user wants to stop current action */ Xchar mode; /* mode of movement */ Xchar gridchar; /* character to use for grid */ Xchar *errorstring; /* error string to type */ Xchar *userlib; /* user's life library if any */ Xjmp_buf ttyjmp; /* jump buffer */ Xstruct input inputs[MAXINPUT]; /* list of input environments */ Xstruct macro macros[26]; /* list of macros */ Xchar stringbuf[FILESIZE]; /* characters for string value */ Xchar rulestring[20]; /* string describing rules */ X Xextern char rules[]; /* life rules */ X Xstruct object *allocobject(); /* allocate new object */ Xstruct object *findobject(); /* find object with certain name */ Xstruct object *getobject(); /* get new object with certain name */ Xstruct row *allocrow(); /* allocate new row */ Xstruct row *findrow(); /* find a row */ Xstruct row *getrow(); /* get a new row */ Xstruct row *computerow(); /* compute a new row */ Xstruct cell *alloccell(); /* allocate new cell */ Xstruct cell *findcell(); /* find a cell */ X Xchar *readstring(); /* read input */ //E*O*F life.h// echo done -- no comment is a comment.
chongo@nsc.UUCP (Landon Noll) (04/12/85)
In net.sources: I am reposting DBell's DPY sources in responce to all the folks who needed them for his LIFE and WAR progs. DBell is no longer on the net (in fact in a few days he will no longer be in this hemisphere) otherwise he would have done this himself. In net.sources.games: I am reposting DBell's LIFE sources complete with the changes for the new DPY. Both LIFE and WAR (which was posted by DBell not long ago) require the use of DPY so be sure and GRAB IT FROM net.sources NOW!!! #---Cut here and place in it's own directory, then feed to Bourne shell--- # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # This archive contains: # alloc.c (2055 chars) # cell.c (3720 chars) # cmd.c (14613 chars) # cmdl.c (11895 chars) # debug.c (1295 chars) # gen.c (4492 chars) # io.c (16636 chars) # echo x - alloc.c sed -e 's/^X//' > "alloc.c" << '//E*O*F alloc.c//' X#ifdef SCCS Xstatic char *sccsid = "@(#)alloc.c 1.5 2/1/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X#endif X X#include "life.h" X X X/* X * Allocate a new object structure. It contains one row element, the termrow. X */ Xstruct object * Xallocobject() X{ X register struct object *obj; X X obj = freeobjects; X if (obj) X freeobjects = obj->o_next; X else { X obj = newobjects; X if (obj >= endobjects) { /* allocate new storage */ X obj = (struct object *) X sbrk(sizeof(struct object) * ALLOCOBJ); X if ((int)obj == -1) { X perror("sbrk"); X exit(1); X } X newobjects = obj; X endobjects = obj + ALLOCOBJ; X } X newobjects++; X } X obj->o_next = NULL; X obj->o_firstrow = termrow; X obj->o_lastrow = NULL; X obj->o_reserved = reserve; X obj->o_name[0] = '\0'; X obj->o_count = 0; X obj->o_gen = 0; X obj->o_lock = 0; X obj->o_currow = 0; X obj->o_curcol = 0; X obj->o_prow = 0; X obj->o_pcol = 0; X obj->o_mark = 0; X obj->o_autoscale = 0; X setscale(obj, 1); X return(obj); X} X X X/* X * Allocate a new row structure. It contains one row element, the termcell. X */ Xstruct row * Xallocrow() X{ X register struct row *rp; X X rp = freerows; X if (rp) X freerows = rp->r_next; X else { X rp = newrows; X if (rp >= endrows) { /* allocate new storage */ X rp = (struct row *) sbrk(sizeof(struct row) * ALLOCROW); X if ((int)rp == -1) { X perror("sbrk"); X exit(1); X } X newrows = rp; X endrows = rp + ALLOCROW; X } X newrows++; X } X rp->r_next = NULL; X rp->r_firstcell = termcell; X rp->r_lastcell = NULL; X rp->r_count = 0; X return(rp); X} X X X/* Allocate a new cell structure */ Xstruct cell * Xalloccell() X{ X register struct cell *cp; X X cp = freecells; X if (cp) { X freecells = cp->c_next; X cp->c_next = NULL; X cp->c_marks = MARK_ANY; X return(cp); X } X cp = newcells; X if (cp >= endcells) { /* allocate new storage */ X cp = (struct cell *) sbrk(sizeof(struct cell) * ALLOCCELL); X if ((int)cp == -1) { X perror("sbrk"); X exit(1); X } X newcells = cp; X endcells = cp + ALLOCCELL; X } X newcells++; X cp->c_next = NULL; X cp->c_marks = MARK_ANY; X return(cp); X} //E*O*F alloc.c// echo x - cell.c sed -e 's/^X//' > "cell.c" << '//E*O*F cell.c//' X#ifdef SCCS Xstatic char *sccsid = "@(#)cell.c 1.3 2/1/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X#endif X X#include "life.h" X X X/* X * Return a row structure for a given row number, returning NULL if not there. X */ Xstruct row * Xfindrow(obj, row) X struct object *obj; X register int row; X{ X register struct row *rp; X X for (rp = obj->o_firstrow; (row > rp->r_row); rp = rp->r_next) ; X if (rp->r_row != row) return(NULL); X return(rp); X} X X X/* X * Return a row structure for a given row number, creating it if needed. X */ Xstruct row * Xgetrow(obj, row) X register struct object *obj; X register int row; X{ X register struct row *rp; /* current row */ X register struct row *nrp; /* next row */ X register struct row *prp; /* previous row */ X X rp = obj->o_firstrow; X if (row < rp->r_row) { /* at front */ X nrp = allocrow(); X nrp->r_row = row; X nrp->r_next = obj->o_firstrow; X obj->o_firstrow = nrp; X if (nrp->r_next == termrow) obj->o_lastrow = nrp; X return(nrp); X } X if (row >= obj->o_lastrow->r_row) rp = obj->o_lastrow; X while (row > rp->r_row) { X prp = rp; X rp = rp->r_next; X } X if (row == rp->r_row) { X return(rp); X } X nrp = allocrow(); X nrp->r_row = row; X nrp->r_next = rp; X prp->r_next = nrp; X if (nrp->r_next == termrow) obj->o_lastrow = nrp; X return(nrp); X} X X X/* X * Find a cell given its coordinates, returning NULL if not found. X */ Xstruct cell * Xfindcell(obj, row, col) X register struct object *obj; X register int col; X{ X register struct row *rp; X register struct cell *cp; X X rp = findrow(obj, row); X if (rp == NULL) return(NULL); X for (cp = rp->r_firstcell; col > cp->c_col; cp = cp->c_next) ; X if (col != cp->c_col) return(NULL); X return(cp); X} X X X/* X * Create a cell at a given row and column. Returns nonzero if the cell X * already existed. If the cell is new, it is marked with the current X * mark value of the object. X */ Xaddcell(obj, row, col) X struct object *obj; X register int col; X{ X register struct row *rp; X register struct cell *cp; /* current cell */ X register struct cell *ncp; /* next cell */ X register struct cell *pcp; /* previous cell */ X X rp = getrow(obj, row); X cp = rp->r_firstcell; X if ((cp != termcell) && (col >= rp->r_lastcell->c_col)) { X pcp = rp->r_lastcell; /* at end */ X if (col == pcp->c_col) return(1); X ncp = alloccell(); X ncp->c_col = col; X ncp->c_marks |= obj->o_mark; X ncp->c_next = termcell; X pcp->c_next = ncp; X rp->r_lastcell = ncp; X rp->r_count++; X obj->o_count++; X return(0); X } X if (col < cp->c_col) { X ncp = alloccell(); /* at front */ X ncp->c_col = col; X ncp->c_marks |= obj->o_mark; X ncp->c_next = cp; X rp->r_firstcell = ncp; X if (cp == termcell) rp->r_lastcell = ncp; X rp->r_count++; X obj->o_count++; X return(0); X } X while (col > cp->c_col) { /* in middle */ X pcp = cp; X cp = pcp->c_next; X } X if (col == cp->c_col) { X return(1); X } X ncp = alloccell(); X ncp->c_col = col; X ncp->c_marks |= obj->o_mark; X ncp->c_next = cp; X pcp->c_next = ncp; X if (cp == termcell) rp->r_lastcell = ncp; X rp->r_count++; X obj->o_count++; X return(0); X} X X X/* X * Delete a cell at a given coordinate. Returns nonzero if it did not exist. X */ Xdelcell(obj, row, col) X register struct object *obj; X register int col; X{ X register struct row *rp; X register struct cell *pcp; /* previous cell */ X register struct cell *cp; /* current cell */ X X rp = findrow(obj, row); X if (rp == NULL) return(1); X pcp = NULL; X cp = rp->r_firstcell; X while (col > cp->c_col) { X pcp = cp; X cp = cp->c_next; X } X if (col != cp->c_col) return(1); X if (pcp) X pcp->c_next = cp->c_next; X else X rp->r_firstcell = cp->c_next; X if (cp->c_next == termcell) rp->r_lastcell = pcp; X cp->c_next = freecells; X freecells = cp; X rp->r_count--; X obj->o_count--; X return(0); X} //E*O*F cell.c// echo x - cmd.c sed -e 's/^X//' > "cmd.c" << '//E*O*F cmd.c//' X#ifdef SCCS Xstatic char *sccsid = "@(#)cmd.c 1.21 2/2/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X#endif X X#include "life.h" X X X/* X * Read commands if available, and execute them. Since we call scanchar for X * our characters, the code after the setjmp can be reentered many times in X * order to finish any command. This allows commands to be typed without X * stopping the computation of generations of an object, and allows editing X * of partially completed commands. X */ Xdocommand() X{ X register int defarg; /* first argument defaulted to one */ X register int ch; /* character read */ X register struct object *obj; /* object being manipulated */ X int arg1; /* first command argument */ X int arg2; /* second command argument */ X int got1; /* got first argument flag */ X int got2; /* got second argument flag */ X char *saveloopptr; /* crock for loop definitions */ X X switch (setjmp(ttyjmp)) { X case SCAN_EDIT: /* command edited before completion */ X curinput->i_endptr = saveloopptr; X break; X case SCAN_EOF: /* not yet enough chars for a command */ X curinput->i_endptr = saveloopptr; X return; X case SCAN_ABORT: /* normal command completion */ X default: /* normal entry point */ X saveloopptr = curinput->i_endptr; X break; X } X if (stop) error("Command aborted"); X obj = curobj; X cmark = 0; X arg2 = 0; X got2 = 0; X ch = readvalue(&arg1, &got1); X if (ch == ',') ch = readvalue(&arg2, &got2); X defarg = 1; X if (got1) defarg = arg1; X switch (ch) { X#ifdef DEBUG X case '\005': /* ^E - show debugging info */ X dumpdata(); X break; X#endif DEBUG X case '\004': /* ^D - end terminal input */ X curinput->i_term(curinput); X break; X case '\014': /* refresh screen */ X dpyredraw(); X redraw = 1; X break; X case '\n': /* move to next "line" */ X crow += defarg; X ccol = pcol; X update = 1; X break; X case '\t': /* move to next tab stop */ X while ((++ccol - pcol) % 8) ; X update = 1; X break; X case ESC: /* execute a macro command */ X ch = scanchar(); X if (ch == ESC) break; /* ignore double escape */ X backup(); X if (setmacro(arg1, arg2, ch)) error("Undefined macro"); X update = 1; X break; X case ' ': /* move to right */ X case '.': X ccol += defarg; X update = 1; X break; X case ':': /* execute line style command */ X case ';': X dolinecommand(arg1, arg2, got1, got2); X break; X case '<': /* begin loop or macro definition */ X if (got1 || got2) { X if (got2) setloop(defarg, arg2, NULL); X else setloop(1, defarg, NULL); X update = 1; X break; X } X ch = scanchar(); /* defining macro */ X if ((ch < 'a') || (ch > 'z')) { X error("Bad macro character"); X } X setloop(1, 1, ch); X update = 1; X break; X case '>': /* end loop */ X endloop(); X break; X case '+': /* increment single char variable */ X ch = scanchar(); X if (ch == '$') ch = scanchar(); X setvariable1(ch, getvariable1(ch) + defarg); X break; X case 'b': /* move lower left with action */ X domove(defarg, -defarg); X break; X case 'B': /* shift to lower left */ X doshift(defarg, -defarg); X break; X case 'c': /* pick cell as current location */ X crow = prow + arg1; X ccol = pcol + arg2; X update = 1; X break; X case 'd': /* delete selection */ X doselect(ch); X backup(); X movemarkedobject(obj, deleteobject, MARK_CMD); X redraw = 1; X break; X case 'f': /* flip selection */ X doselect(ch); X cmark = MARK_USR; X flipmarkedobject(obj, MARK_CMD); X redraw = 1; X break; X case 'g': /* compute generations */ X if (obj->o_lock) error("Object locked"); X if (genleft <= 0) backup(); X genleft = (genleft ? arg1 : defarg); X freqcount = frequency; X update = 1; X break; X case 'G': /* compute infinite generations */ X if (obj->o_lock) error("Object locked"); X if (genleft <= 0) backup(); X genleft = INFINITY; X freqcount = frequency; X update = 1; X break; X case 'h': /* move left with action */ X domove(0, -defarg); X break; X case 'H': /* shift left lots */ X doshift(0, -defarg); X break; X case 'i': /* toggle insert mode */ X checkrun(); X if (mode == M_MOVE) mode = M_INSERT; X else if (mode == M_INSERT) mode = M_DELETE; X else mode = M_MOVE; X update = 1; X break; X case 'j': /* move down with action */ X domove(defarg, 0); X break; X case 'J': /* shift down lots */ X doshift(defarg, 0); X break; X case 'k': /* move up with action */ X domove(-defarg, 0); X break; X case 'K': /* shift up lots */ X doshift(-defarg, 0); X break; X case 'l': /* move right with action */ X domove(0, defarg); X break; X case 'L': /* shift right lots */ X doshift(0, defarg); X break; X case 'm': /* mark current object */ X doselect(ch); X copymarks(obj, MARK_CMD, MARK_USR); X redraw = 1; X break; X case 'M': /* remove all marks */ X checkrun(); X clearmarks(obj, MARK_SEE); X redraw = 1; X break; X case 'n': /* move lower right with action */ X domove(defarg, defarg); X break; X case 'N': /* shift down and right */ X doshift(defarg, defarg); X break; X case 'o': /* insert new cells */ X case 'O': X checkrun(); X backup(); X while ((stop == 0) && (defarg-- > 0)) { X addcell(obj, crow, ccol++); X } X redraw = 1; X break; X case 'p': /* place deleted object */ X checkrun(); X backup(); X cmark = MARK_USR; X addobject(deleteobject, obj, RELATIVE); X redraw = 1; X break; X case 'r': /* rotate selection */ X doselect(ch); X cmark = MARK_USR; X rotatemarkedobject(obj, MARK_CMD); X redraw = 1; X break; X case 's': /* set scale factor and center object */ X obj->o_autoscale = 0; X if (got1 == 0) arg1 = obj->o_scale; X setscale(obj, arg1); X break; X case 'S': /* perform auto-scaling */ X if (got1 == 0) arg1 = obj->o_scale; X setscale(obj, arg1); X obj->o_autoscale = 1; X redraw = 1; X break; X case 't': /* toggle current cell */ X checkrun(); X backup(); X if (delcell(obj, crow, ccol)) X addcell(obj, crow, ccol); X redraw = 1; X break; X case 'u': /* move upper right with action */ X domove(-defarg, defarg); X break; X case 'U': /* shift to upper right */ X doshift(-defarg, defarg); X break; X case 'x': /* kill current cells */ X checkrun(); X backup(); X while ((stop == 0) && (defarg-- > 0)) { X delcell(obj, crow, ccol++); X } X redraw = 1; X break; X case 'y': /* move upper left with action */ X domove(-defarg, -defarg); X break; X case 'Y': /* shift to upper left */ X doshift(-defarg, -defarg); X break; X case 'z': /* clear or set generation number */ X checkrun(); X obj->o_gen = arg1; X obj->o_born = 0; X obj->o_died = 0; X update = 1; X break; X case '/': /* search for next object */ X { X int minr, maxr, minc, maxc; X X checkrun(); X if (searchobject(obj, defarg, 0)) error("Empty object"); X clearmarks(obj, MARK_CMD); X markobject(obj, crow, ccol, MARK_CMD); X markminmax(obj, MARK_CMD, &minr, &maxr, &minc, &maxc); X positionview(minr, maxr, minc, maxc); X update = 1; X } X break; X case '@': /* point at current location */ X prow = crow; X pcol = ccol; X break; X case '!': /* comment characters */ X case '#': X while (scanchar() != '\n') ; X break; X default: /* unknown commands */ X error("Unknown command"); X } X scanabort(); /* completed command */ X} X X X/* X * Read a numeric value (if any) to be used as an argument for a command. X * Pointers to the returned value and returned flag are given. X * The returned value is zero if no value is read. X * The returned flag is nonzero if a value was read. X * Return value is the first non-argument character read. X */ Xreadvalue(valueptr, flagptr) X register int *valueptr; /* pointer to returned value */ X register int *flagptr; /* pointer to got value flag */ X{ X register int ch; /* character being read */ X register struct input *ip; /* input structure */ X int sign; /* sign of result */ X X *valueptr = 0; X *flagptr = 0; X sign = 1; X ch = scanchar(); X if (ch == '-') { /* negative value */ X sign = -1; X ch = scanchar(); X } X if (ch == '$') { /* get variable value */ X *valueptr = sign * getvariable1(scanchar()); X *flagptr = 1; X return(scanchar()); X } X if (ch == '(') { /* get expression */ X *valueptr = sign * scanexpr(); X *flagptr = 1; X return(scanchar()); X } X while ((ch >= '0') && (ch <= '9')) { /* get numeric value */ X *valueptr = (*valueptr * 10) + ch - '0'; X *flagptr = 1; X ch = scanchar(); X } X if (ch == '%') { /* get loop value */ X ch = 1; X if (*flagptr) ch = *valueptr; X if (ch <= 0) error("Bad nest value"); X ip = curinput + 1; X while (ch > 0) { X if (--ip < inputs) error("Bad nest value"); X if (ip->i_type == INP_LOOP) ch--; X } X *valueptr = ip->i_curval; X *flagptr = 1; X ch = scanchar(); X } X *valueptr *= sign; X return(ch); X} X X X/* X * Routine called from above to scan and evaluate a parenthesized expression. X * This routines knows that one parenthesis has already been read. Stops X * reading on the matching parenthesis. X */ Xscanexpr() X{ X register char *cp; /* current character */ X register int nest; /* nesting depth */ X char buf[100]; /* expression buffer */ X X cp = buf; X *cp++ = '('; /* start with parenthesis */ X nest = 1; X while (nest > 0) { X if (cp >= &buf[sizeof(buf)-2]) error("expression too long"); X *cp = scanchar(); X if (*cp == '(') nest++; X if (*cp == ')') nest--; X cp++; X } X *cp = '\0'; X return(getexpression(buf)); X} X X X/* X * Select a set of cells to be used for some command. This involves reading X * the next character and marking cells based on that character. Repeating X * the command character is equivilant to selecting the current object. On a X * successful return, exactly those cells specified are marked with MARK_CMD. X * If no cells are found, an error is generated. X */ Xdoselect(cmd) X{ X register struct object *obj; /* object to examine */ X register long minrow, maxrow, mincol, maxcol; /* range for marks */ X register struct cell *cp; /* current cell */ X int ch; /* character to select on */ X X checkrun(); X ch = scanchar(); X if (ch == cmd) ch = 'o'; /* repeated char is connected object */ X minrow = -INFINITY; X maxrow = -minrow; X mincol = minrow; X maxcol = maxrow; X obj = curobj; X clearmarks(obj, MARK_CMD); X switch (ch) { X case 'a': /* all of object */ X break; X case 'b': /* below and left of cursor */ X minrow = crow; X maxcol = ccol; X break; X case 'c': /* current cell */ X cp = findcell(obj, crow, ccol); X if (cp == NULL) error("No cell at current location"); X cp->c_marks |= MARK_CMD; X return; X case 'h': /* left of cursor */ X maxcol = ccol; X break; X case 'j': /* below cursor */ X minrow = crow; X break; X case 'k': /* above cursor */ X maxrow = crow; X break; X case 'l': /* right of cursor */ X mincol = ccol; X break; X case 'm': /* marked cells */ X if (copymarks(obj, MARK_USR, MARK_CMD)) X error("No object marked"); X return; X case 'n': /* below and right of cursor */ X minrow = crow; X mincol = ccol; X break; X case 'o': /* connected object */ X if (markobject(obj, crow, ccol, MARK_CMD)) X error("No object at current location"); X return; X case 'p': /* rectangle to pointer */ X minrow = crow; X maxrow = prow; X if (minrow > maxrow) { X minrow = prow; X maxrow = crow; X } X mincol = ccol; X maxcol = pcol; X if (mincol > maxcol) { X mincol = pcol; X maxcol = ccol; X } X break; X case 'u': /* above and right of cursor */ X maxrow = crow; X mincol = ccol; X break; X case 'v': /* things visible in window */ X minrow = obj->o_minrow; X maxrow = obj->o_maxrow; X mincol = obj->o_mincol; X maxcol = obj->o_maxcol; X break; X case 'y': /* above and left of cursor */ X maxrow = crow; X maxcol = ccol; X break; X default: /* unknown */ X error("Unknown selection command"); X } X if (markregion(obj, MARK_CMD, minrow, maxrow, mincol, maxcol) == 0) X error("No cells in region"); X} X X X/* X * Move the current position by the indicated deltas, performing the X * current action to the configuration. The movement is scaled by X * the current scaling factor. X */ Xdomove(rowdelta, coldelta) X register int rowdelta; /* amount to change row by */ X register int coldelta; /* amount to change column by */ X{ X register int row1; /* increment for row */ X register int col1; /* increment for column */ X X rowdelta *= curobj->o_scale; X coldelta *= curobj->o_scale; X if (mode == M_MOVE) { /* just want to move */ X crow += rowdelta; X ccol += coldelta; X update = 1; X return; X } X checkrun(); X backup(); X row1 = 0; /* need to loop for insert or delete */ X col1 = 0; X if (rowdelta > 0) row1 = 1; X if (rowdelta < 0) row1 = -1; X if (coldelta > 0) col1 = 1; X if (coldelta < 0) col1 = -1; X rowdelta += crow; X coldelta += ccol; X while ((stop == 0) && ((crow != rowdelta) || (ccol != coldelta))) { X crow += row1; X ccol += col1; X switch (mode) { X case M_INSERT: X addcell(curobj, crow, ccol); X break; X case M_DELETE: X delcell(curobj, crow, ccol); X } X } X redraw = 1; X} X X X/* X * Shift the window lots in the indicated direction, and also shift the X * cursor location the same amount so that the cursor location on the X * screen doesn't change. "Lots" is 1/4 of the screen width or height. X * Special case: if both x and y are being shifted, we shift both by the X * same amount, which is the minimum of the two shifts. X */ Xdoshift(rowdelta, coldelta) X register int rowdelta; /* amount to change row by */ X register int coldelta; /* amount to change column by */ X{ X register struct object *obj; /* current object */ X register int rowsign; /* sign of row */ X register int colsign; /* sign of column */ X X obj = curobj; X rowdelta *= ((rowradius * obj->o_scale) / 2); X coldelta *= ((colradius * obj->o_scale) / 2); X if (rowdelta && coldelta) { /* take minimums of absolute values */ X rowsign = 1; X colsign = 1; X if (rowdelta < 0) { X rowsign = -1; X rowdelta = -rowdelta; X } X if (coldelta < 0) { X colsign = -1; X coldelta = -coldelta; X } X if (rowdelta > coldelta) rowdelta = coldelta; X if (coldelta > rowdelta) coldelta = rowdelta; X rowdelta *= rowsign; X coldelta *= colsign; X } X obj->o_currow += rowdelta; X obj->o_minrow += rowdelta; X obj->o_maxrow += rowdelta; X obj->o_curcol += coldelta; X obj->o_mincol += coldelta; X obj->o_maxcol += coldelta; X redraw = 1; X} X X X/* X * Perform a backup of the current object. This is only done if reading X * from the terminal, since it is from the point of view of the user. X */ Xbackup() X{ X if (curinput->i_type == INP_TTY) copyobject(curobj, backupobject); X} X X X/* X * Check to see that generations are not being computed before proceeding X * with the current command. X */ Xcheckrun() X{ X if (genleft > 0) error("Illegal while running"); X} //E*O*F cmd.c// echo x - cmdl.c sed -e 's/^X//' > "cmdl.c" << '//E*O*F cmdl.c//' X#ifdef SCCS Xstatic char *sccsid = "@(#)cmdl.c 1.31 2/2/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X#endif X X#include "life.h" X#include <sys/ioctl.h> X X/* list of line command routines */ Xint c_create(), c_destroy(), c_edit(), c_dumpmacros(), c_quit(); Xint c_frequency(), c_listobjects(), c_zero(), c_lock(), c_unlock(); Xint c_read(), c_write(), c_ttyinput(), c_gridchar(), c_nogrid(); Xint c_insert(), c_copy(), c_copyselect(), c_move(), c_moveselect(); Xint c_help(), c_rules(), c_endinputlevel(), c_undo(), c_rename(); Xint c_set(), c_variables(), c_type(), c_update(), c_wait(); X X/* flags for line commands */ X#define F_NONE 0x0 /* no special condition */ X#define F_NOARG 0x1 /* must not have an argument */ X#define F_ARG1 0x2 /* must have at least one argument */ X#define F_NORUN 0x4 /* illegal if running generations */ X#define F_UPDATE 0x8 /* update status if command completes */ X#define F_REDRAW 0x10 /* redraw screen if command completes */ X#define F_ABBR 0x20 /* abbreviation works even if ambiguous */ X X X/* X * Dispatch table for line commands. Those commands which are ambiguous X * must be defined so as to be contiguous in the table. A spaces delimits X * the command itself from the help string for the command. X */ Xstruct cmdtab { X char *c_name; /* command name */ X int (*c_func)(); /* function to call */ X int c_flags; /* flags for command */ X} cmdtab[] = { X "copy (current object to) obj", c_copy, F_ARG1|F_NORUN|F_ABBR, X "copyselection (to) obj", c_copyselect, F_ARG1|F_NORUN, X "create (object named) obj", c_create, F_ARG1|F_NORUN|F_REDRAW, X "destroy (object named) obj", c_destroy, F_ARG1|F_NORUN, X "dumpmacros (to) file", c_dumpmacros, F_ARG1|F_NORUN, X "edit (object named) obj", c_edit, F_NORUN|F_REDRAW|F_ABBR, X "endinputlevel", c_endinputlevel, F_NOARG|F_UPDATE, X "frequency (of typeout is) expr", c_frequency, F_UPDATE, X "gridcharacter (is) char", c_gridchar, F_REDRAW, X "help", c_help, F_NONE|F_ABBR, X "insert (object from) obj", c_insert, F_ARG1|F_NORUN|F_REDRAW|F_ABBR, X "list (all objects)", c_listobjects, F_NORUN|F_ABBR, X "lock (current object)", c_lock, F_NOARG|F_NORUN|F_UPDATE, X "move (current object to) obj", c_move, F_ARG1|F_NORUN|F_REDRAW|F_ABBR, X "moveselection (to) obj",c_moveselect,F_ARG1|F_NORUN|F_REDRAW, X "nogrid", c_nogrid, F_NOARG|F_REDRAW, X "quit (program)", c_quit, F_NOARG|F_ABBR, X "read (commands from) file", c_read, F_ARG1|F_NORUN|F_REDRAW|F_ABBR, X "rename (current object to) obj", c_rename, F_ARG1|F_UPDATE, X "rules (for life are) born live", c_rules, F_NORUN|F_UPDATE, X "set (variable) name (to) expr", c_set, F_ARG1|F_ABBR, X "ttyinput", c_ttyinput, F_REDRAW, X "type (value of expression) expr", c_type, F_ARG1|F_UPDATE|F_ABBR, X "undo (last change)", c_undo, F_NOARG|F_NORUN|F_REDRAW|F_ABBR, X "unlock (current object)", c_unlock, F_NOARG|F_NORUN|F_UPDATE, X "updateview (to be current)", c_update, F_NOARG, X "variables (are listed)", c_variables, F_NOARG|F_REDRAW|F_ABBR, X "wait (for computations)", c_wait, F_NOARG, X "write (current object to) file", c_write, F_ARG1|F_NORUN|F_ABBR, X "zero (current object)", c_zero, F_NOARG|F_NORUN|F_REDRAW|F_ABBR, X 0 /* ends the table */ X}; X X X/* X * Read and execute a command line style command. This kind of command echoes, X * and is terminated by an end of line. Numeric arguments are available. X */ Xdolinecommand(arg1, arg2, got1, got2) X{ X register char *name; /* command name */ X register char *args; /* arguments for command */ X register struct cmdtab *cmd; /* command structure */ X register int flag; /* flags for command */ X X name = readstring("command: "); X if ((*name == '\0') || (*name == '\n')) return; X for (args = name; *args && (*args != ' ') && (*args != '\t'); args++) ; X while ((*args == ' ') || (*args == '\t')) *args++ = '\0'; X for (cmd = cmdtab; ; cmd++) { X if (cmd->c_name == NULL) error("Unknown line command"); X if (abbrev(name, cmd[0].c_name) == 0) continue; X if (cmd->c_flags & F_ABBR) break; X if (abbrev(name, cmd[1].c_name) == 0) break; X if (strcmp(name, cmd[0].c_name) == 0) break; X if (cmd[1].c_flags & F_ABBR) continue; X error("Ambiguous line command"); X } X flag = cmd->c_flags; X if (flag & F_NORUN) checkrun(); X if ((flag & F_ARG1) && (*args == '\0')) error("Missing argument"); X if ((flag & F_NOARG) && *args) error("Argument not allowed"); X cmd->c_func(args, arg1, arg2, got1, got2); X if (flag & F_UPDATE) update = 1; X if (flag & F_REDRAW) redraw = 1; X} X X X X/* Copy the current object into another object */ Xc_copy(cp) X char *cp; /* destination object name */ X{ X copyobject(curobj, getobject(cp)); X} X X X/* Copy the current selection into another object */ Xc_copyselect(cp) X char *cp; /* destination object name */ X{ X copymarkedobject(curobj, getobject(cp), MARK_USR); X} X X X/* Edit an object, creating it if necessary */ Xc_create(cp) X char *cp; /* object to create */ X{ X setobject(getobject(cp)); X} X X X/* Destroy an existing object */ Xc_destroy(cp) X char *cp; /* object name */ X{ X register struct object *obj; /* object to destroy */ X X obj = findobject(cp); X if (obj == NULL) error("No such object"); X destroyobject(obj); X} X X X/* Dump list of macros to file */ Xc_dumpmacros(cp) X char *cp; /* file name */ X{ X writemacros(cp); X} X X X/* Edit an existing object. A null argument implies the previous object. */ Xc_edit(cp) X char *cp; /* object name */ X{ X register struct object *obj; /* object to edit */ X X obj = prevobj; X if (*cp) obj = findobject(cp); X if (obj == NULL) error("No such object"); X setobject(obj); X} X X X/* Undo the last change made to the current object */ Xc_undo() X{ X moveobject(curobj, tempobject); X moveobject(backupobject, curobj); X moveobject(tempobject, backupobject); X} X X X/* End current input level */ Xc_endinputlevel() X{ X if (curinput <= inputs) error("Cannot end top level input"); X curinput->i_term(curinput); X} X X X/* Update the view even if inside of a loop or macro */ Xc_update() X{ X update = 1; X updateview(); X} X X X/* Wait until all outstanding generation computations are finished */ Xc_wait() X{ X while ((stop == 0) && (genleft > 0)) { X dogeneration(curobj); X updateview(); X } X} X X X/* Set output frequency */ Xc_frequency(cp) X register char *cp; /* frequency string */ X{ X register int freq; /* new frequency */ X X freq = 1; X if (*cp) freq = getexpression(cp); X if (freq <= 0) error("Illegal value"); X frequency = freq; X freqcount = freq; X} X X X/* Select the character used for the background of the screen */ Xc_gridchar(cp) X register char *cp; /* grid character string */ X{ X if (*cp == '\0') cp = "."; X if ((*cp < ' ') || (cp[1] != '\0')) error("Bad grid character"); X gridchar = *cp; X} X X X/* Set so we don`t see a grid on the screen */ Xc_nogrid() X{ X gridchar = ' '; X} X X X/* Type list of line commands */ Xc_help() X{ X register struct cmdtab *cmd; /* command structure */ X register int count; X X dpywindow(0, -1, 0, -1); X dpystr("\ XThe following table lists all line mode commands. Unique abbreviations are\n\ Xallowed. Commands shown with '*' can be abbreviated even when ambiguous.\n"); X count = 0; X for (cmd = cmdtab; cmd->c_name; cmd++) { X if ((count++ % 2) == 0) dpychar('\n'); X dpyprintf("%c %-35s", ((cmd->c_flags & F_ABBR) ? '*' : ' '), X cmd->c_name); X } X dpychar('\n'); X spacewait(); X} X X X/* Insert another object into this one */ Xc_insert(cp) X char *cp; /* object name */ X{ X register struct object *obj; /* object to insert */ X X obj = findobject(cp); X if (obj == NULL) error("No such object"); X cmark = MARK_USR; X backup(); X addobject(obj, curobj, RELATIVE); X cmark = MARK_ANY; X} X X X/* Show list of objects */ Xc_listobjects(cp) X register char *cp; /* option string */ X{ X int all; /* true if want to see all objects */ X X all = ((*cp == '-') || (*cp == 'a')); X listobjects(all); X} X X X/* Lock object so it can't be run */ Xc_lock() X{ X curobj->o_lock = 1; X genleft = 0; X freqcount = frequency; X} X X X/* Unlock object so it can be run */ Xc_unlock() X{ X curobj->o_lock = 0; X} X X X/* Rename the current object to something else */ Xc_rename(cp) X register char *cp; /* new name */ X{ X if (curobj->o_reserved) error("Cannot rename reserved object"); X if (strlen(cp) > MAXNAME) error("Name too long"); X if (findobject(cp)) error("Name already exists"); X if (BADNAME(cp)) error("Cannot create reserved name"); X strcpy(curobj->o_name, cp); X} X X X/* Move current object into another object */ Xc_move(cp) X char *cp; /* destination object name */ X{ X moveobject(curobj, getobject(cp)); X} X X X/* Move current selection into another object */ Xc_moveselect(cp) X char *cp; /* destination object name */ X{ X movemarkedobject(curobj, getobject(cp), MARK_USR); X} X X X/* Set the value of a variable */ Xc_set(cp) X register char *cp; /* variable name */ X{ X register char *exp; /* expression */ X X for (exp = cp; *exp && (*exp != ' ') && (*exp != '='); exp++) ; X if (*exp == ' ') { /* skip spaces */ X *exp++ = '\0'; X while (*exp == ' ') exp++; X } X if (*exp == '\0') { /* no expression, set to zero */ X setvariable(cp, 0); X return; X } X if (*exp == '=') *exp++ = '\0'; X setvariable(cp, getexpression(exp)); X} X X X/* Type the value of an expression */ Xc_type(cp) X char *cp; /* expression */ X{ X static char buf[20]; /* storage for string */ X X sprintf(buf, "%d\n", getexpression(cp));/* store value */ X errorstring = buf; /* make it seen */ X} X X X/* Display the values of all the variables */ Xc_variables() X{ X listvariables(); X} X X X/* Quit program */ Xc_quit() X{ X dpyclose(); X exit(0); X} X X X/* Read commands or object from file, defaulting extension if needed */ Xc_read(cp) X register char *cp; /* file name */ X{ X backup(); X if (setfile(cp)) error("Cannot open input file"); X} X X X/* Set new life rules */ Xc_rules(cp) X register char *cp; /* life rules string */ X{ X register char *bp; /* born string */ X register char *lp; /* live string */ X X if (*cp == '\0') cp = "3,23"; X bp = cp; X while ((*cp >= '0') && (*cp <= '8')) cp++; X if (*cp == '\0') error("Missing rule string"); X if ((*cp != ',') && (*cp != ' ') && (*cp != '\t')) { X error("Bad born string"); X } X *cp++ = '\0'; X while ((*cp == ',') || (*cp == ' ') || (*cp == '\t')) cp++; X lp = cp; X while ((*cp >= '0') && (*cp <= '8')) cp++; X if (*cp != '\0') error("Bad live string"); X for (cp = rules; cp < &rules[18]; cp++) *cp = 0; X while (*bp) rules[*bp++ - '0'] = 1; X while (*lp) rules[*lp++ - '0' + LIFE] = 1; X bp = rulestring; X for (cp = rules; cp < &rules[LIFE]; cp++) { X if (*cp) *bp++ = '0' + (cp - rules); X } X *bp++ = ','; X for (cp = &rules[LIFE]; cp < &rules[18]; cp++) { X if (*cp) *bp++ = ('0' - LIFE) + (cp - rules); X } X *bp = '\0'; X} X X X/* X * Read commands from the terminal. Useful in loops or command files. X * If the -c argument is given, we don't do it if no input is ready. X */ Xc_ttyinput(cp) X register char *cp; /* pointer to argument string */ X{ X int count; /* character count */ X X if ((*cp == '-') || (*cp == 'c')) { /* check for input */ X count = 0; X ioctl(STDIN, FIONREAD, &count); X if (count == 0) return; X } X if (settty()) error("Nesting too deep"); X} X X X/* Write object to file */ Xc_write(cp, arg1, arg2, got1, got2) X register char *cp; /* pointer to argument string */ X{ X if (got1 == 0) arg1 = WRITECOLS; X writeobject(curobj, cp, arg1?WRITEROWS:0, arg1); X} X X X/* Zero out current object */ Xc_zero() X{ X register struct object *obj; /* current object */ X X obj = curobj; X if (obj->o_lock) error("Object is locked"); X backup(); X zeroobject(obj); X obj->o_gen = 0; X obj->o_born = 0; X obj->o_died = 0; X obj->o_currow = 0; X obj->o_curcol = 0; X obj->o_prow = 0; X obj->o_pcol = 0; X obj->o_autoscale = 0; X setscale(obj, 1); X mode = M_MOVE; X frequency = 1; X freqcount = 1; X} X X X/* X * See if one string is an abbreviation of another. This knows that X * the second string contains spaces which terminate the comparison. X * Returns nonzero if first string is an abbreviation. X */ Xabbrev(str1, str2) X register char *str1, *str2; /* strings */ X{ X if ((str1 == NULL) || (str2 == NULL)) return(0); X while (*str1) if (*str1++ != *str2++) return(0); X return(1); X} //E*O*F cmdl.c// echo x - debug.c sed -e 's/^X//' > "debug.c" << '//E*O*F debug.c//' X#ifdef SCCS Xstatic char *sccsid = "@(#)debug.c 1.2 1/14/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X#endif X X#ifdef DEBUG X#include "life.h" X X/* X * Subroutine to dump out all object data structures for debugging. X */ Xdumpdata() X{ X register struct object *obj; X X printf("\ntermrow %x termcell %x\n", termrow, termcell); X for (obj = objects; obj; obj = obj->o_next) { X dumpobj(obj); X } X printf("done\n"); X} X X X/* X * Dump out an object. X */ Xdumpobj(obj) X register struct object *obj; X{ X register struct row *rp; X X printf("object %s (%x) firstrow %x lastrow %x\n", X obj->o_name, obj, obj->o_firstrow, obj->o_lastrow); X rp = obj->o_firstrow; X while (1) { X dumprow(rp); X if (rp == termrow) break; X rp = rp->r_next; X } X} X X X/* X * Dump out a row X */ Xdumprow(rp) X register struct row *rp; X{ X register struct cell *cp; X X if (rp == termrow) { X printf(" termrow\n"); X return; X } X printf(" row %d (%x) firstcell %x lastcell %x\n", X rp->r_row, rp, rp->r_firstcell, rp->r_lastcell); X cp = rp->r_firstcell; X while (1) { X dumpcell(cp); X if (cp == termcell) break; X cp = cp->c_next; X } X} X X X/* X * Dump out a cell X */ Xdumpcell(cp) X register struct cell *cp; X{ X if (cp == termcell) { X printf(" termcell\n"); X return; X } X printf(" cell %d (%x)\n", cp->c_col, cp); X} X X#endif DEBUG //E*O*F debug.c// echo x - gen.c sed -e 's/^X//' > "gen.c" << '//E*O*F gen.c//' X#ifdef SCCS Xstatic char *sccsid = "@(#)gen.c 1.4 2/2/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X#endif X X#include "life.h" X Xchar rules[18] = {0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0}; /* life rules */ X X X/* X * Compute one full generation of the current configuration. X * This is done by calling computerow with each triple of rows which X * contain any live cells, and remembering the result. When all rows X * are finished, we change the object. X */ Xdogeneration(obj) X struct object *obj; /* object to compute */ X{ X register struct row *prp; /* previous row */ X register struct row *crp; /* current row */ X register struct row *nrp; /* next row */ X register struct row *newrp; /* current row of new list */ X register struct row *srp; /* saved row pointer */ X int row; /* current row number */ X X if (genleft <= 0) return; X if ((--genleft == 0) || (--freqcount <= 0)) redraw = 1; X obj->o_gen++; X obj->o_born = 0; X obj->o_died = 0; X newrp = &initrow; X prp = termrow; X crp = termrow; X nrp = obj->o_firstrow; X srp = nrp->r_next; X row = nrp->r_row - 1; X while (1) { /* loop over each triple of rows */ X if (row >= (INFINITY-1)) break; X prp = computerow(obj, prp, crp, nrp); X if (prp) { /* have something in current row */ X newrp->r_next = prp; X newrp = prp; X newrp->r_row = row; X obj->o_count += prp->r_count; X } X row++; X prp = crp; X crp = nrp; X nrp = srp; X if (nrp->r_row == row + 1) { X srp = nrp->r_next; X continue; X } X nrp = termrow; X if ((prp != termrow) || (crp != termrow)) continue; X nrp = srp; X srp = nrp->r_next; X row = nrp->r_row - 1; X } X zeroobject(obj); X if (newrp == &initrow) { /* died off */ X genleft = 0; X redraw = 1; X return; X } X obj->o_firstrow = initrow.r_next; X newrp->r_next = termrow; X obj->o_lastrow = newrp; X if ((obj->o_born == 0) && (obj->o_died == 0)) { /* no change */ X genleft = 0; X redraw = 1; X } X} X X X/* X * Compute the result of three adjacent rows, and return a row structure X * containing the new middle row, or NULL if no live cells are produced. X * When determining if a cell is dead or alive, each live neighbor counts X * as a 1, but the current cell counts as 9 when alive. Indexing the rules X * table with the sum then automatically produces the correct result. X */ Xstruct row * Xcomputerow(obj, prevrow, currow, nextrow) X struct object *obj; X struct row *prevrow, *currow, *nextrow; X{ X register struct cell *pcp; /* head of previous row of cells */ X register struct cell *ccp; /* head of current row of cells */ X register struct cell *ncp; /* head of next row of cells */ X register struct cell *tcp; /* temporary cell */ X register struct cell *newcp; /* new row of cells */ X register int i; /* sum of live cells and other uses */ X struct row *rp; /* new row pointer */ X int col; /* current column being examined */ X int colp1; /* one more than column */ X int count; /* live cells */ X X pcp = prevrow->r_firstcell; X ccp = currow->r_firstcell; X ncp = nextrow->r_firstcell; X newcp = &initcell; X count = 0; X col = -INFINITY; X while (1) { /* loop over cells of all 3 rows */ X /* X * Find next column where a cell exists on any row X */ X i = col - 1; X while (i > pcp->c_col) pcp = pcp->c_next; X while (i > ccp->c_col) ccp = ccp->c_next; X while (i > ncp->c_col) ncp = ncp->c_next; X i = ccp->c_col; X if (pcp->c_col < i) i = pcp->c_col; X if (ncp->c_col < i) i = ncp->c_col; X if (i == INFINITY) break; X i--; X if (col < i) col = i; X i = 0; X colp1 = col + 1; X if (pcp->c_col <= colp1) { /* add cells in previous row */ X i++; X tcp = pcp->c_next; X i += (tcp->c_col <= colp1); X tcp = tcp->c_next; X i += (tcp->c_col <= colp1); X } X if (ccp->c_col <= colp1) { /* add cells on our row */ X i++; X tcp = ccp->c_next; X if ((ccp->c_col == col) || (tcp->c_col == col)) { X i += (LIFE - 1); X } X i += (tcp->c_col <= colp1); X tcp = tcp->c_next; X i += (tcp->c_col <= colp1); X } X if (ncp->c_col <= colp1) { /* add cells in next row */ X i++; X tcp = ncp->c_next; X i += (tcp->c_col <= colp1); X tcp = tcp->c_next; X i += (tcp->c_col <= colp1); X } X if (rules[i]) { /* cell is alive */ X obj->o_born += (i < LIFE); X tcp = alloccell(); X tcp->c_col = col; X newcp->c_next = tcp; X newcp = tcp; X count++; X } else /* cell is dead */ X obj->o_died += (i >= LIFE); X col++; X } X if (newcp == &initcell) return(NULL); X newcp->c_next = termcell; X rp = allocrow(); X rp->r_firstcell = initcell.c_next; X rp->r_lastcell = newcp; X rp->r_count = count; X return(rp); X} //E*O*F gen.c// echo x - io.c sed -e 's/^X//' > "io.c" << '//E*O*F io.c//' X#ifdef SCCS Xstatic char *sccsid = "@(#)io.c 1.10 2/1/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X#endif X X#include "life.h" X#include <errno.h> X#include <sys/ioctl.h> X Xextern int errno; /* error return value */ X Xint tty_char(), tty_term(); /* terminal routines */ Xint file_char(), file_term(); /* file routines */ Xint loop_char(), loop_term(); /* loop routines */ Xint macro_char(), macro_term(); /* macro routines */ Xint getdpychar(); /* for dpyread to call */ X X X/* X * Read the next character from the appropriate input source. X * Negative answer from source indicates end of file. X * This routine is usually called indirectly by scanchar. X */ Xreadchar() X{ X register struct input *ip; /* input structure being used */ X register int ch; /* current character */ X X while (1) { /* continue until not end of file */ X ip = curinput; X ch = ip->i_getchar(ip); /* read next character */ X if (ch >= 0) break; X ip->i_term(ip); /* end of file, clean up */ X } X return(ch); X} X X X/* X * Determine if the next input character to be read is from the terminal. X * Returns nonzero if so. X */ Xttyisinput() X{ X register struct input *ip; /* input structure being checked */ X X ip = curinput; X while ((ip->i_type == INP_LOOP) && (ip->i_first)) ip--; X return(ip->i_type == INP_TTY); X} X X X/* X * Setup to read commands from the terminal. The lowest level of input X * never quits, and is unusual in that it doesn't usually block waiting for X * input. Returns nonzero if failed. X */ Xsettty() X{ X register struct input *ip; /* current input */ X X ip = curinput + 1; /* allocate next structure */ X if (ip >= &inputs[MAXINPUT]) { X return(1); X } X ip->i_getchar = tty_char; /* set up for I/O */ X ip->i_term = tty_term; X ip->i_type = INP_TTY; X update = 1; X curinput = ip; X return(0); X} X X X/* X * Read next character from the terminal if it is ready. If nothing is X * going on we will wait for it anyway, to prevent excessive runtimes. X * We set the interactive flag to indicate we are talking to user. X */ Xtty_char() X{ X long n; /* char count */ X int ch; /* char to return */ X X interact = 1; X if ((dowait == 0) && (redraw || update || (genleft > 0))) { X if ((ioctl(STDIN, FIONREAD, &n) == 0) && (n <= 0)) { X scaneof(); /* no char available now */ X } X } X do { X if (stop) return('\0'); /* stop will be seen later */ X errno = 0; X n = read(STDIN, &ch, 1); /* read one char */ X } while ((n < 0) && (errno == EINTR)); X if (n <= 0) { X return(-1); /* error or end of file */ X } X if (errorstring) { /* disable error message */ X errorstring = NULL; X update = 1; X } X return(ch &= 0x7f); X} X X X/* X * Terminate reading from the terminal. If we are reading from the lowest X * level of terminal input, this is a no-op. X */ Xtty_term(ip) X register struct input *ip; /* input structure */ X{ X if (ip > inputs) ip--; X curinput = ip; X update = 1; X} X X X/* X * Setup to read commands from a given file name. When the file is complete, X * some parameters (like the current cursor position) will be restored to their X * original value. Returns nonzero if cannot set up to read the file. X */ Xsetfile(name) X register char *name; /* file name to read from */ X{ X register struct input *ip; /* current input structure */ X X ip = curinput + 1; /* use next structure */ X if (ip >= &inputs[MAXINPUT]) { X return(1); X } X ip->i_file = openlibfile(name, 0); /* open the file */ X if (ip->i_file < 0) { X return(1); X } X ip->i_begptr = (char *) malloc(FILESIZE); /* allocate data buffer */ X if (ip->i_begptr == NULL) { X close(ip->i_file); X return(1); X } X ip->i_endptr = ip->i_begptr; /* set up rest of io */ X ip->i_curptr = ip->i_begptr; X ip->i_getchar = file_char; X ip->i_term = file_term; X ip->i_type = INP_FILE; X ip->i_obj = curobj; /* save current state */ X ip->i_row = crow; X ip->i_col = ccol; X ip->i_prow = prow; X ip->i_pcol = pcol; X prow = crow; X pcol = ccol; X update = 1; X curinput = ip; /* this is now current input */ X return(0); X} X X X/* X * Open a life library file, trying various transformed names if necessary. X * The order of the transformations which are tried is: X * 1. Name exactly as given. X * 2. Name with ".l" appended to it. X * 3. Name in the user's library directory. X * 4. Name with ".l" appended to it in the user's library. X * 5 Name in the system's library directory. X * 6. Name with ".l" appended to it in the system library. X * Returns the file descriptor if the open is successful, or -1 if all the X * open attempts failed. X */ Xopenlibfile(name, mode) X register char *name; /* original file name */ X register int mode; /* desired open mode */ X{ X register int fd; /* file descriptor */ X char buf[2000]; /* transformed names */ X X fd = open(name, mode); /* try name straight */ X if (fd >= 0) return(fd); X sprintf(buf, "%s.l", name); /* try name with .l appended */ X fd = open(buf, mode); X if (fd >= 0) return(fd); X if (*name == '/') return(-1); /* quit now if absolute name */ X if (userlib) { /* look in user's lib area */ X sprintf(buf, "%s/%s", userlib, name); X fd = open(buf, mode); X if (fd >= 0) return(fd); X strcat(buf, ".l"); /* try with .l */ X fd = open(buf, mode); X if (fd >= 0) return(fd); X } X sprintf(buf, "%s/%s", LIFELIB, name); /* look in general lib area */ X fd = open(buf, mode); X if (fd >= 0) return(fd); X strcat(buf, ".l"); /* last try with .l */ X return(open(buf, mode)); X} X X X/* X * Here to read next character from a file. Called by readchar when X * input source is a file. X */ Xfile_char(ip) X register struct input *ip; /* input structure */ X{ X if (ip->i_curptr >= ip->i_endptr) { /* get next chunk of file */ X ip->i_curptr = ip->i_begptr; X ip->i_endptr = ip->i_begptr; X ip->i_endptr += read(ip->i_file, ip->i_begptr, FILESIZE); X if (ip->i_endptr <= ip->i_begptr) { X return(-1); /* end of file */ X } X } X return(*ip->i_curptr++ & 0x7f); X} X X X/* X * Here on end of file or error to close the input file and restore some X * of the previous state such as the cursor location. X */ Xfile_term(ip) X register struct input *ip; /* input structure */ X{ X close(ip->i_file); X free(ip->i_begptr); X curobj = ip->i_obj; X crow = ip->i_row; X ccol = ip->i_col; X prow = ip->i_prow; X pcol = ip->i_pcol; X update = 1; X curinput = (ip - 1); X} X X X/* X * Setup for execution of a loop. This remembers the initial and final X * loop values, and sets up to remember commands as they are given. X * This is also used for defining a macro command. The given character X * will be assigned the string defined by the loop. X * Returns nonzero if failed. X */ Xsetloop(begval, endval, ch) X{ X register struct input *ip; /* input structure */ X X ip = curinput + 1; /* allocate next structure */ X if (ip >= &inputs[MAXINPUT]) { X return(1); X } X ip->i_begptr = (char *) malloc(LOOPSIZE); /* allocate buffer */ X if (ip->i_begptr == NULL) { X return(1); X } X ip->i_endptr = ip->i_begptr + LOOPSIZE; /* set up for I/O */ X ip->i_curptr = ip->i_begptr; X ip->i_first = 1; X ip->i_getchar = loop_char; X ip->i_term = loop_term; X ip->i_type = INP_LOOP; X ip->i_curval = begval; X ip->i_endval = endval; X ip->i_macro = ch; X update = 1; X curinput = ip; X return(0); X} X X X/* X * End the range of the currently defined loop. At this point, all of X * the characters of the loop have been read in and saved, and we can X * just proceed to iterate over them. The next read will find out that X * the first iteration of the loop is over. X */ Xendloop() X{ X register struct input *ip; /* current input */ X X ip = curinput; X if (ip->i_type != INP_LOOP) error("Loop not being defined"); X ip->i_endptr = ip->i_curptr - 1; /* end before loop term cmd */ X ip->i_first = 0; X} X X X/* X * Read one character from a loop buffer. If at the end of the buffer, the X * pointer is reset so that the buffer is reread. When enough iterations X * have been processed, we are done. A special case exists the first time X * through the loop at the first nesting level, in that we don't yet have X * the characters necessary for the loop, and so we have to read them by X * ourself. X */ Xloop_char(ip) X register struct input *ip; /* input structure */ X{ X register int ch; X X if (ip->i_first) { /* collecting input chars */ X if (ip->i_curptr >= ip->i_endptr) error("Loop too long"); X ch = ip[-1].i_getchar(ip - 1); /* char from previous level */ X if (ch < 0) error("End of file in loop"); X *ip->i_curptr++ = ch; X return(ch); X } X if (ip->i_curptr >= ip->i_endptr) { /* done with one iteration */ X if (ip->i_curval == ip->i_endval) { X return(-1); /* end of file */ X } X ip->i_curval += ((ip->i_curval < ip->i_endval) ? 1 : -1); X ip->i_curptr = ip->i_begptr; X } X return(*ip->i_curptr++); X} X X X/* X * Terminate reading from a loop buffer. If this was the definition of X * a macro character, remember it for later. X */ Xloop_term(ip) X struct input *ip; /* input structure */ X{ X register struct macro *mp; /* macro being defined */ X X mp = ¯os[ip->i_macro - 'a']; X if ((mp >= macros) && (mp < ¯os[26])) { X if (mp->m_begptr) free(mp->m_begptr); X mp->m_begptr = ip->i_begptr; X mp->m_endptr = ip->i_endptr; X } else X free(ip->i_begptr); /* or free buffer */ X update = 1; X curinput = (ip - 1); X} X X X/* Set up to read a defined macro command. Returns nonzero if failed. */ Xsetmacro(arg1, arg2, ch) X{ X register struct input *ip; /* current input */ X register struct macro *mp; /* macro command */ X X mp = ¯os[ch - 'a']; /* verify macro character */ X if ((mp < macros) || (mp >= ¯os[26]) || (mp->m_begptr == NULL)) { X return(1); X } X ip = curinput + 1; /* use next input structure */ X if (ip >= &inputs[MAXINPUT]) { X return(1); X } X ip->i_getchar = macro_char; /* set up for I/O */ X ip->i_term = macro_term; X ip->i_begptr = mp->m_begptr; X ip->i_curptr = mp->m_begptr; X ip->i_endptr = mp->m_endptr; X ip->i_type = INP_MACRO; X ip->i_macro = ch; X ip->i_curval = arg1; X ip->i_endval = arg2; X update = 1; X curinput = ip; X return(0); X} X X X/* Here to read next character from macro definition. */ Xmacro_char(ip) X register struct input *ip; /* input structure */ X{ X if (ip->i_curptr >= ip->i_endptr) { X return(-1); /* end of file */ X } X return(*ip->i_curptr++); X} X X X/* Here to terminate reading from a macro definition. */ Xmacro_term(ip) X struct input *ip; /* input structure */ X{ X curinput = (ip - 1); X update = 1; X} X X X/* X * Read a line from the user terminated by a newline (which is removed). X * Editing of the input line is fully handled. The prompt string and the X * input line are only visible if the current input is from the terminal. X * Returns a pointer to the null-terminated string. X */ Xchar * Xreadstring(prompt) X register char *prompt; /* prompt string */ X{ X int i; /* number of characters read */ X X scanreset(); /* no more scan interference */ X if (prompt == NULL) prompt = ""; /* set prompt if given NULL */ X if (ttyisinput() == 0) prompt = NULL; /* no window stuff if not tty */ X if (prompt) dpywindow(0, 0, 0, -1); /* show input in top line */ X dowait = 1; /* must wait for tty chars */ X i = dpyread(prompt, getdpychar, stringbuf, FILESIZE); /* read it */ X dowait = 0; /* back to normal */ X stringbuf[i] = '\0'; /* terminate line */ X update = 1; X return(stringbuf); X} X X X/* X * Routine called by dpyread to read the next character of input. X * We return end of input on the newline character. X */ Xgetdpychar() X{ X register int ch; /* character just read */ X X ch = readchar(); X if (stop || (ch == '\n')) return(-1); X return(ch); X} X X X/* X * Routine called to wait until a space, newline, or escape character X * is typed. It is assumed that the screen contains a display which X * we can append our message to. X */ Xspacewait() X{ X register int ch; /* read character */ X X scanreset(); /* throw out stored chars */ X dpystr("\nType <space> to return\n"); /* append our message */ X dpyclrwindow(); X dpyhome(); X dpyupdate(); /* show the result */ X redraw = 1; X dowait = 1; /* must wait for chars */ X do { X if (stop) break; X ch = readchar(); X } while ((ch != ' ') && (ch != ESC) && (ch != '\n')); X dowait = 0; X} X X X/* X * Write the current object out to the named file, with the given maximum X * sizes for "pretty" output. If the size is exceeded, the output is X * compressed. The file as written can be read in as commands which will X * regenerate the object. X */ Xwriteobject(obj, name, maxrows, maxcols) X struct object *obj; /* object to write */ X char *name; /* filename to use */ X{ X register FILE *fd; /* file structure */ X register struct row *rp; /* current row structure */ X register struct cell *cp; /* current cell */ X register int row; /* current row value */ X register int col; /* current column value */ X struct row *trp; /* temporary row structure */ X int minrow; /* minimum row of object */ X int maxrow; /* maximum row of object */ X int mincol; /* minimum column of object */ X int maxcol; /* maximum column of object */ X int curmin; /* current minimum column */ X int curmax; /* current maximum column */ X int testmin; /* test minimum column of rows */ X int testmax; /* test maximum column of rows */ X X minmax(obj, &minrow, &maxrow, &mincol, &maxcol); X if (minrow > maxrow) error("Null object"); /* nothing to write */ X fd = fopen(name, "w"); X if (fd == NULL) error("Cannot open output file"); X fprintf(fd, "! \"%s\" (cells %d length %d width %d generation %d)\n", X obj->o_name, obj->o_count, maxrow - minrow + 1, X maxcol - mincol + 1, obj->o_gen); X if (obj->o_currow > minrow) fprintf(fd, "%dk", obj->o_currow - minrow); X if (obj->o_currow < minrow) fprintf(fd, "%dj", minrow - obj->o_currow); X if (obj->o_curcol > mincol) fprintf(fd, "%dh", obj->o_curcol - mincol); X if (obj->o_curcol < mincol) fprintf(fd, "%dl", mincol - obj->o_curcol); X fprintf(fd, "@!\n"); X curmin = INFINITY; X curmax = -INFINITY; X rp = obj->o_firstrow; X row = minrow; X for (; rp != termrow; rp = rp->r_next) { X /* X * See if user wants to stop. X */ X if (stop) { X fclose(fd); X return; X } X /* X * Skip down to the next row with something in it. X */ X if (rp->r_firstcell == termcell) continue; X if (rp->r_row > (row + maxrows)) { /* skip to right row */ X fprintf(fd, "%d\n", rp->r_row - row); X row = rp->r_row; X } X while (rp->r_row > row) { X fputs(".\n", fd); X row++; X } X /* X * Output the current row, compressing if it is too wide. X */ X col = mincol; X cp = rp->r_firstcell; X if ((cp->c_col + maxcols) < rp->r_lastcell->c_col) { X while (cp != termcell) { X /* X * Write all adjacent blanks. X */ X if (cp->c_col > col + 2) { X fprintf(fd, "%d.", cp->c_col - col); X col = cp->c_col; X } X while (cp->c_col > col) { X fputc('.', fd); X col++; X } X /* X * Write all adjacent cells. X */ X while ((cp->c_col + 1) == cp->c_next->c_col) { X cp = cp->c_next; X } X if (cp->c_col >= col + 2) { X fprintf(fd, "%dO", cp->c_col - col + 1); X col = cp->c_col + 1; X } X while (col <= cp->c_col) { X fputc('O', fd); X col++; X } X cp = cp->c_next; X } X fputc('\n', fd); X row++; X curmin = INFINITY; X curmax = -INFINITY; X continue; X } X /* X * Here if the row doesn't need compressing. See if several X * rows from this one on can all fit in the same range. If X * so, set things up so they will align themselves nicely. X */ X if ((cp->c_col < curmin) || (rp->r_lastcell->c_col > curmax)) { X curmin = cp->c_col; X curmax = rp->r_lastcell->c_col; X trp = rp; X for (; rp != termrow; rp = rp->r_next) { X if (rp->r_firstcell == termcell) continue; X testmin = rp->r_firstcell->c_col; X if (testmin > curmin) testmin = curmin; X testmax = rp->r_lastcell->c_col; X if (testmax < curmax) testmax = curmax; X if ((testmax - testmin) >= maxcols) break; X curmin = testmin; X curmax = testmax; X } X rp = trp; X } X /* X * Type the row, with the initial shift if necessary. X */ X if (curmin != mincol) { X fprintf(fd, "%d.", curmin - mincol); X col = curmin; X } X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) { X while (cp->c_col > col) { X fputc('.', fd); X col++; X } X fputc('O', fd); X col++; X } X fputc('\n', fd); X row++; X } X fclose(fd); X} X X X X/* Write all defined macros to a file so they can be read back in later. */ Xwritemacros(name) X char *name; /* file name to write to */ X{ X register int f; /* file descriptor */ X register struct macro *mp; /* current macro */ X int ch; /* character */ X X f = creat(name, 0666); X if (f < 0) error("Cannot create file"); X for (mp = macros; mp < ¯os[26]; mp++) { X if ((mp->m_begptr == NULL) || (mp->m_begptr >= mp->m_endptr)) { X continue; X } X ch = 'a' + (mp - macros); X write(f, "<", 1); X write(f, &ch, 1); X write(f, mp->m_begptr, mp->m_endptr - mp->m_begptr); X write(f, ">!\n", 3); X } X close(f); X} //E*O*F io.c// echo done -- no comment is a comment.
chongo@nsc.UUCP (Landon Noll) (04/12/85)
In net.sources: I am reposting DBell's DPY sources in responce to all the folks who needed them for his LIFE and WAR progs. DBell is no longer on the net (in fact in a few days he will no longer be in this hemisphere) otherwise he would have done this himself. In net.sources.games: I am reposting DBell's LIFE sources complete with the changes for the new DPY. Both LIFE and WAR (which was posted by DBell not long ago) require the use of DPY so be sure and GRAB IT FROM net.sources NOW!!! ---------------------begin DBell's comments------------------------------- This is the last part of the actual life sources. The final article just contains life objects. #---Cut here and place in it's own directory, then feed to Bourne shell--- # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # This archive contains: # main.c (2819 chars) # mark.c (9268 chars) # object.c (8845 chars) # scan.c (3526 chars) # vars.c (9419 chars) # view.c (9529 chars) # echo x - main.c sed -e 's/^X//' > "main.c" << '//E*O*F main.c//' Xstatic char *sccsid = "@(#)main.c 1.12 2/2/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X X/* X * The game of life on an infinite board (by David I. Bell). X * These life sources are in the public domain, and can be copied X * or used as desired, with the following restrictions: X * 1. All copyright notices (and this notice) must be preserved. X * 2. The life sources (even if modified) cannot be sold for profit. X * 3. If any sources are modified, a sentence must exist by the X * copyright notice of each modified source file which says that X * the file has been modified. X */ X X#include "life.h" X#include <sgtty.h> X Xint intint(), readchar(); /* routines */ Xchar *getenv(); /* another one */ X X Xmain(argc, argv) X char **argv; /* argument is a life object */ X{ X userlib = getenv(LIFEVAR); X strcpy(rulestring, "3,23"); X termcell = alloccell(); X termcell->c_next = termcell; X termcell->c_col = INFINITY; X termrow = allocrow(); X termrow->r_next = termrow; X termrow->r_firstcell = termcell; X termrow->r_row = INFINITY; X mode = M_MOVE; X gridchar = ' '; X frequency = 1; X freqcount = 1; X rowradius = 1; /* temp until find real sizes */ X colradius = 1; X reserve = 1; /* creating special objects */ X deleteobject = getobject("..delete"); X tempobject = getobject("..temp"); X backupobject = getobject("..backup"); X mainobject = getobject("main"); X curobj = mainobject; X prevobj = mainobject; X reserve = 0; /* no more special objects */ X curinput = &inputs[-1]; /* initialize for tty input */ X settty(); X if ((argc > 1) && setfile(argv[1])) { /* set to get input file */ X perror(argv[1]); X exit(1); X } X if (dpyinit((char *)0, (char *)0)) { /* home up and clear screen */ X exit(1); X } X dpymove(-1, -1); /* get screen size */ X rowradius = (dpygetrow() - 1) / 2; X colradius = (dpygetcol() - 1) / 2; X setscale(deleteobject, 1); /* fix scale factors now */ X setscale(tempobject, 1); X setscale(backupobject, 1); X setscale(mainobject, 1); X signal(SIGINT, intint); X scaninit(readchar, ttyjmp); X while (1) { X docommand(); X dogeneration(curobj); X updateview(); X } X} X X X/* X * Here on an interrupt character. Remember to stop what we are doing soon. X * We cannot just longjmp away since things may be in an inconsistent state. X */ Xintint() X{ X signal(SIGINT, intint); /* not needed in 4.2, but so what */ X genleft = 0; X stop = 1; X redraw = 1; X#ifdef DEBUG X dumpdata(); X#endif DEBUG X} X X X/* X * Here on an error. Close all but the top input level, cancel the X * current command, beep, and set up to display the indicated message. X * The message will remain until the next command is typed by the user. X */ Xerror(str) X char *str; /* message to type */ X{ X while (curinput > inputs) curinput->i_term(curinput); X errorstring = str; X redraw = 1; X stop = 0; X dowait = 0; X write(STDERR, "\007", 1); X scanabort(); X} //E*O*F main.c// echo x - mark.c sed -e 's/^X//' > "mark.c" << '//E*O*F mark.c//' X#ifdef SCCS Xstatic char *sccsid = "@(#)mark.c 1.7 2/2/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X#endif X X#include "life.h" X Xstruct loc { /* structure to hold row and column pairs */ X long l_row; X long l_col; X}; X Xchar rowwalk[9] = {-1,0,0,1,1,0,0,-1,0}; /* row deltas to walk a cell */ Xchar colwalk[9] = {-1,1,1,0,0,-1,-1,0,1}; /* col deltas to walk a cell */ X X X/* X * Mark the object at a given location as specified. Returns nonzero if X * no object exists there. An object for this purpose is considered as a X * king-wise connected set of live cells. X */ Xmarkobject(obj, row, col, mark) X register struct object *obj; /* object to mark up */ X{ X register struct cell *cp; /* object being marked */ X X cp = findcell(obj, row, col); X if (cp == NULL) return(1); X cp->c_marks |= mark; X markloop(obj, row, col, mark); X return(0); X} X X X/* Recursive subroutine called from markobject */ Xmarkloop(obj, row, col, mark) X struct object *obj; /* object begin marked */ X register int row; /* current row */ X register int col; /* current column */ X register int mark; /* marking value */ X{ X register struct cell *cp; /* current cell */ X register struct loc *rp; /* pointer into list table */ X int i; /* to iterate over directions */ X struct loc rclist[8]; /* row and column list */ X X while (1) { X if (stop) return; X rp = rclist; X for (i = 0; i < 8; i++) { /* find neighbors */ X row += rowwalk[i]; X col += colwalk[i]; X cp = findcell(obj, row, col); X if (cp == NULL) continue; X if (cp->c_marks & mark) continue; X cp->c_marks |= mark; X rp->l_row = row; X rp->l_col = col; X rp++; X } X if (--rp != rclist) { /* recurse if more than one */ X for (; rp >= rclist; rp--) { X markloop(obj, rp->l_row, rp->l_col, mark); X } X return; X } X row = rp->l_row; /* else follow single cell */ X col = rp->l_col; X } X} X X X/* Mark a whole object as specified. */ Xsetmarks(obj, mark) X struct object *obj; /* object to mark */ X register int mark; /* mark to be applied */ X{ X register struct row *rp; /* row pointer */ X register struct cell *cp; /* cell pointer */ X X mark |= MARK_ANY; X for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) { X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) { X cp->c_marks |= mark; X } X } X} X X X/* X * Copy the marks from one type to another for an object. Returns nonzero X * if there were no cells with the given mark. X */ Xcopymarks(obj, srcmark, destmark) X struct object *obj; /* object to mark */ X register int srcmark; /* mark to be found */ X register int destmark; /* mark to be set */ X{ X register struct row *rp; /* row pointer */ X register struct cell *cp; /* cell pointer */ X int error; X X error = 1; X for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) { X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) { X if ((cp->c_marks & srcmark) == 0) continue; X cp->c_marks |= destmark; X error = 0; X } X } X return(error); X} X X X/* Clear marks for a whole object as specified. */ Xclearmarks(obj, mark) X struct object *obj; /* object to mark */ X register int mark; /* mark to be cleared */ X{ X register struct row *rp; /* row pointer */ X register struct cell *cp; /* cell pointer */ X X mark = ~mark; X mark |= MARK_ANY; X for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) { X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) { X cp->c_marks &= mark; X } X } X} X X X/* X * Mark the cells in a specified rectangular region as desired. X * Returns the number of cells which were marked. X */ Xmarkregion(obj, mark, minrow, maxrow, mincol, maxcol) X struct object *obj; /* object to mark */ X register int mark; /* value to mark with */ X long minrow, maxrow, mincol, maxcol; /* range to mark */ X{ X register struct row *rp; /* current row */ X register struct cell *cp; /* current cell */ X register long count; /* count of cells */ X X count = 0; X for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) { X if (rp->r_row < minrow) continue; X if (rp->r_row > maxrow) break; X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) { X if (cp->c_col < mincol) continue; X if (cp->c_col > maxcol) break; X cp->c_marks |= mark; X count++; X } X } X return(count); X} X X X/* X * Find the range of all marked cells for an object. Returns nonzero if X * no cells were marked. X */ Xmarkminmax(obj, mark, minrow, maxrow, mincol, maxcol) X struct object *obj; /* object to search */ X register int mark; /* mark to search for */ X long *minrow, *maxrow, *mincol, *maxcol; /* results */ X{ X register struct row *rp; /* current row */ X register struct cell *cp; /* current cell */ X register int row; /* current row */ X long minr, maxr, minc, maxc; /* temp variables */ X X minr = INFINITY; X maxr = -INFINITY; X minc = INFINITY; X maxc = -INFINITY; X for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) { X row = rp->r_row; X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) { X if ((cp->c_marks & mark) == 0) continue; X if (row < minr) minr = row; X if (row > maxr) maxr = row; X if (cp->c_col < minc) minc = cp->c_col; X if (cp->c_col > maxc) maxc = cp->c_col; X } X } X if (minr > maxr) return(1); X *minrow = minr; X *maxrow = maxr; X *mincol = minc; X *maxcol = maxc; X return(0); X} X X X/* Count the number of marked cells in an object */ Xcountmarks(obj, mark) X struct object *obj; /* object to check */ X register int mark; /* mark to be counted */ X{ X register struct row *rp; /* row pointer */ X register struct cell *cp; /* cell pointer */ X register long count; /* number of cells */ X X count = 0; X for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) { X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) { X if (cp->c_marks & mark) count++; X } X } X return(count); X} X X X/* X * Move marked cells to another object. If the destination object is NULL X * the cells are just deleted. The previous cells of the destination object X * are deleted. X */ Xmovemarkedobject(sobj, dobj, mark) X register struct object *sobj; /* source object */ X struct object *dobj; /* destination object */ X{ X register struct row *rp; /* row pointer */ X register struct cell *cp; /* current cell pointer */ X register struct cell *pcp; /* previous cell pointer */ X register struct cell *ncp; /* next cell pointer */ X X if (sobj == dobj) error("Moving object to itself"); X if (dobj) { X zeroobject(dobj); X dobj->o_currow = sobj->o_currow; X dobj->o_curcol = sobj->o_curcol; X } X for (rp = sobj->o_firstrow; rp != termrow; rp = rp->r_next) { X pcp = NULL; X cp = rp->r_firstcell; X while (cp != termcell) { X if ((cp->c_marks & mark) == 0) { X pcp = cp; X cp = cp->c_next; X continue; X } X if (dobj) addcell(dobj, rp->r_row, cp->c_col); X ncp = cp->c_next; X if (pcp == NULL) X rp->r_firstcell = ncp; X else X pcp->c_next = ncp; X if (ncp == termcell) rp->r_lastcell = pcp; X cp->c_next = freecells; X freecells = cp; X cp = ncp; X rp->r_count--; X sobj->o_count--; X } X } X} X X X/* X * Copy marked cells to another object. The previous cells of the destination X * object are deleted. The source object is unaffected. X */ Xcopymarkedobject(sobj, dobj, mark) X register struct object *sobj; /* source object */ X register struct object *dobj; /* destination object */ X register int mark; /* mark value */ X{ X register struct row *rp; /* row pointer */ X register struct cell *cp; /* current cell */ X X if (sobj == dobj) error("Copying object to itself"); X zeroobject(dobj); X dobj->o_currow = sobj->o_currow; X dobj->o_curcol = sobj->o_curcol; X for (rp = sobj->o_firstrow; rp != termrow; rp = rp->r_next) { X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) { X if ((cp->c_marks & mark) == 0) continue; X addcell(dobj, rp->r_row, cp->c_col); X } X } X} X X X/* X * Rotate the marked cells in the given object around the current cursor X * location. This deletes the marked cells, and reinserts them after X * their position has been rotated by 90 degrees clockwise. X */ Xrotatemarkedobject(obj, mark) X register struct object *obj; /* current object */ X{ X register struct row *rp; /* current row */ X register struct cell *cp; /* current cell */ X register int row, col; /* current row and column */ X X if (obj == tempobject) error("Using temp object"); X movemarkedobject(obj, tempobject, mark); X for (rp = tempobject->o_firstrow; rp != termrow; rp = rp->r_next) { X row = rp->r_row - obj->o_currow; X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) { X col = cp->c_col - obj->o_curcol; X addcell(obj, obj->o_currow + col, obj->o_curcol - row); X } X } X} X X X/* X * Flip the marked cells in the given object around the column of the current X * cursor location. This deletes the marked cells, and reinserts them after X * their position has been flipped around the vertical axis. X */ Xflipmarkedobject(obj, mark) X register struct object *obj; /* current object */ X{ X register struct row *rp; /* current row */ X register struct cell *cp; /* current cell */ X register int row, col; /* current row and column */ X X if (obj == tempobject) error("Using temp object"); X movemarkedobject(obj, tempobject, mark); X for (rp = tempobject->o_firstrow; rp != termrow; rp = rp->r_next) { X row = rp->r_row; X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) { X col = cp->c_col - obj->o_curcol; X addcell(obj, row, obj->o_curcol - col); X } X } X} //E*O*F mark.c// echo x - object.c sed -e 's/^X//' > "object.c" << '//E*O*F object.c//' X#ifdef SCCS Xstatic char *sccsid = "@(#)object.c 1.17 2/2/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X#endif X X#include "life.h" X X X/* X * Find the given named object. Returns NULL if nonexistant. The special X * name of "." means the current object. The special name of ".." means X * the previous object. X */ Xstruct object * Xfindobject(str) X register char *str; /* name to find */ X{ X register struct object *obj; /* current object */ X X if (str[0] == '.') { /* check for "." or ".." */ X if (str[1] == '\0') return(curobj); X if ((str[1] == '.') && (str[2] == '\0')) return(prevobj); X } X for (obj = objects; obj; obj = obj->o_next) { X if (strcmp(obj->o_name, str) == 0) return(obj); X } X return(NULL); X} X X X/* Create the given named object, or return it if it already exists. */ Xstruct object * Xgetobject(str) X register char *str; /* name to find */ X{ X register struct object *obj; /* current object */ X X for (obj = objects; obj; obj = obj->o_next) { X if (strcmp(obj->o_name, str) == 0) return(obj); X } X if (strlen(str) > MAXNAME) error("Object name too long"); X if ((reserve==0) && BADNAME(str)) error("Cannot create reserved name"); X obj = allocobject(); X obj->o_next = objects; X objects = obj; X strcpy(obj->o_name, str); X return(obj); X} X X X/* X * Set an object as the current one. The old current object is remembered X * so that it can be referenced using "..". This cancels any insert mode. X */ Xsetobject(obj) X register struct object *obj; /* new object to set */ X{ X mode = M_MOVE; X if (obj == curobj) return; X prevobj = curobj; X curobj = obj; X redraw = 1; X} X X X/* Delete all cells of an object */ Xzeroobject(obj) X register struct object *obj; X{ X register struct row *rp; X X rp = obj->o_firstrow; X if (rp == termrow) return; X for (; rp != termrow; rp = rp->r_next) { X if (rp->r_firstcell == termcell) continue; X rp->r_lastcell->c_next = freecells; X freecells = rp->r_firstcell; X obj->o_count -= rp->r_count; X } X obj->o_lastrow->r_next = freerows; X freerows = obj->o_firstrow; X obj->o_firstrow = termrow; X obj->o_lastrow = NULL; X} X X X/* X * Destroy the existence of an object. If it is the current object, X * switch the current object back to the previous object. X */ Xdestroyobject(obj) X register struct object *obj; /* object to delete */ X{ X register struct object *pobj; /* previous object */ X X if (obj == NULL) return; X if (obj->o_reserved) error("Cannot destroy reserved object"); X if (obj == prevobj) prevobj = mainobject; X if (obj == curobj) { X curobj = prevobj; X prevobj = mainobject; X redraw = 1; X } X zeroobject(obj); X if (objects == obj) { /* first object in list */ X objects = obj->o_next; X obj->o_next = freeobjects; X freeobjects = obj; X return; X } X for (pobj = objects; pobj->o_next != obj; pobj = pobj->o_next) ; X pobj->o_next = obj->o_next; X obj->o_next = freeobjects; X freeobjects = obj; X} X X X/* X * Move one object to another. The source object is zeroed, and the X * previous contents of the destination object are lost. X */ Xmoveobject(sobj, dobj) X register struct object *sobj; /* source object */ X register struct object *dobj; /* destination object */ X{ X if (sobj == dobj) error("Moving object to itself"); X zeroobject(dobj); X dobj->o_currow = sobj->o_currow; X dobj->o_curcol = sobj->o_curcol; X dobj->o_minrow = sobj->o_minrow; X dobj->o_maxrow = sobj->o_maxrow; X dobj->o_mincol = sobj->o_mincol; X dobj->o_maxcol = sobj->o_maxcol; X dobj->o_scale = sobj->o_scale; X dobj->o_autoscale = sobj->o_autoscale; X dobj->o_prow = sobj->o_prow; X dobj->o_pcol = sobj->o_pcol; X dobj->o_firstrow = sobj->o_firstrow; X dobj->o_lastrow = sobj->o_lastrow; X dobj->o_count = sobj->o_count; X sobj->o_firstrow = termrow; X sobj->o_lastrow = NULL; X sobj->o_count = 0; X} X X X/* X * Add one object to another. The source object is unchanged. The X * destination object will get all cells from both objects. If disp is X * RELATIVE, the object is displaced as specified by the two object's cursor X * positions. Otherwise, the addition is performed with absolute coordinates. X */ Xaddobject(sobj, dobj, disp) X register struct object *sobj; /* source object */ X register struct object *dobj; /* destination object */ X{ X register struct row *rp; /* current row */ X register struct cell *cp; /* current cell */ X register int newrow; /* new row number */ X int rowdisp, coldisp; /* displacements */ X X if (sobj == dobj) error("Adding object to itself"); X rowdisp = 0; X coldisp = 0; X if (disp == RELATIVE) { X rowdisp = dobj->o_currow - sobj->o_currow; X coldisp = dobj->o_curcol - sobj->o_curcol; X } X for (rp = sobj->o_firstrow; rp != termrow; rp = rp->r_next) { X newrow = rp->r_row + rowdisp; X for (cp = rp->r_firstcell; cp != termcell; cp = cp->c_next) { X addcell(dobj, newrow, cp->c_col + coldisp); X } X } X} X X X/* X * Copy one object to another. The source object is unchanged. X * The current contents of the destination object are lost. X */ Xcopyobject(sobj, dobj) X register struct object *sobj; /* source object */ X register struct object *dobj; /* destination object */ X{ X if (sobj == dobj) error("Copying object to itself"); X zeroobject(dobj); X addobject(sobj, dobj, ABSOLUTE); X dobj->o_currow = sobj->o_currow; X dobj->o_curcol = sobj->o_curcol; X dobj->o_minrow = sobj->o_minrow; X dobj->o_maxrow = sobj->o_maxrow; X dobj->o_mincol = sobj->o_mincol; X dobj->o_maxcol = sobj->o_maxcol; X dobj->o_scale = sobj->o_scale; X dobj->o_autoscale = sobj->o_autoscale; X dobj->o_prow = sobj->o_prow; X dobj->o_pcol = sobj->o_pcol; X} X X X/* X * Show the list of objects. If all is nonzero, all objects will be X * shown. Otherwise, only objects not starting with a period are shown. X */ Xlistobjects(all) X{ X register struct object *obj; /* current object */ X register int ch; /* current character */ X int minrow, maxrow, mincol, maxcol; /* current bounds */ X X dpywindow(0, -1, 0, -1); X dpystr("cells height width gen scale object\n"); X dpystr("----- ------ ----- --- ----- ------\n"); X for (obj = objects; obj; obj = obj->o_next) { X if ((all == 0) && (obj->o_name[0] == '.')) continue; X ch = ' '; X if (obj == prevobj) ch = '+'; X if (obj == curobj) ch = '*'; X minmax(obj, &minrow, &maxrow, &mincol, &maxcol); X dpyprintf("%d\t%d\t%d\t%d\t%d\t%c %s\n", X obj->o_count, (maxrow - minrow + 1), X (maxcol - mincol + 1), obj->o_gen, X obj->o_scale, ch, obj->o_name); X } X dpyprintf("\n\ XIn object column, '*' = current object, '+' = previous object\n"); X if (all == 0) X dpyprintf("Use -a to show objects beginning with '.'\n"); X spacewait(); X} X X X/* X * Find the minimum and maximum row and column numbers for an object. X * If there are no cells in the object, the mins will be one more than X * the maxes. Returns nonzero if the object has no cells. X */ Xminmax(obj, minrow, maxrow, mincol, maxcol) X struct object *obj; /* object to examine */ X long *minrow, *maxrow, *mincol, *maxcol; /* pointers to result */ X{ X register struct row *rp; /* current row */ X register int maxr, minr, maxc, minc; /* current results */ X int err; /* return value */ X X minr = INFINITY; X maxr = -INFINITY; X minc = INFINITY; X maxc = -INFINITY; X err = 1; X for (rp = obj->o_firstrow; rp != termrow; rp = rp->r_next) { X if (rp->r_firstcell == termcell) continue; X if (rp->r_row < minr) minr = rp->r_row; X maxr = rp->r_row; X if (rp->r_firstcell->c_col<minc) minc = rp->r_firstcell->c_col; X if (rp->r_lastcell->c_col>maxc) maxc = rp->r_lastcell->c_col; X err = 0; X } X if (err) { /* no cells in object */ X minr = 1; X maxr = 0; X minc = 1; X maxc = 0; X } X *minrow = minr; X *maxrow = maxr; X *mincol = minc; X *maxcol = maxc; X return(err); X} X X X/* X * Search forwards for the nth next object, restarting at the top if necessary. X * If all is nonzero, or if wrap around occurs, the search will be over all X * objects. Otherwise, objects found in previous searches will be skipped. X * Returns nonzero if nothing was found. X */ Xsearchobject(obj, count, all) X register struct object *obj; /* object to search through */ X{ X register struct row *rp; /* current row being examined */ X register struct cell *cp; /* current cell begin examined */ X register long row; /* current row */ X register long col; /* current column */ X X if (all) clearmarks(obj, MARK_SRC); X row = obj->o_currow; X col = obj->o_curcol; X for (rp = obj->o_firstrow; row > rp->r_row; rp = rp->r_next) ; X for (cp = rp->r_firstcell; col > cp->c_col; cp = cp->c_next) ; X if ((row == rp->r_row) && (col == cp->c_col) && X ((cp->c_marks & MARK_SRC) == 0)) count++; X while (1) { X if (stop) return(0); X if (cp == termcell) { X rp = rp->r_next; X if (rp == termrow) { X clearmarks(obj, MARK_SRC); X rp = obj->o_firstrow; X } X cp = rp->r_firstcell; X continue; X } X if ((cp->c_marks & MARK_SRC) == 0) { X markobject(obj, rp->r_row, cp->c_col, MARK_SRC); X if (--count <= 0) break; X } X cp = cp->c_next; X } X obj->o_currow = rp->r_row; X obj->o_curcol = cp->c_col; X return(0); X} //E*O*F object.c// echo x - scan.c sed -e 's/^X//' > "scan.c" << '//E*O*F scan.c//' X#ifdef SCCS Xstatic char *sccsid = "@(#)scan.c 1.4 1/27/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X#endif X X/* Module to read self-terminating input while allowing editing of the input */ X X#include <sgtty.h> X#include "life.h" X Xjmp_buf *scanjumpbuf; /* jump buffer to use in scanchar */ Xchar scanbuffer[SCAN_SIZE+1]; /* storage for characters */ Xchar *scanreadptr; /* current read pointer */ Xchar *scanwriteptr; /* current write pointer */ Xchar (*scanroutine)(); /* routine to read characters */ Xstatic char rubchar; /* erase letter character */ Xstatic char rubword; /* erase word character */ Xstatic char rubline; /* erase line character */ Xstatic char litchar; /* literal input */ X X X/* X * Initialize for later calls to scanchar. X */ Xscaninit(routine, jumpbuf) X char (*routine)(); /* routine to get characters */ X jmp_buf *jumpbuf; /* jump buffer to use later */ X{ X struct sgttyb sgbuf; /* basic tty structure */ X struct ltchars ltbuf; /* local tty structure */ X X scanroutine = routine; /* init static variables */ X scanjumpbuf = jumpbuf; X scanwriteptr = scanbuffer; X scanreadptr = scanbuffer; X sgbuf.sg_erase = CERASE; /* set defaults in case ioctls fail */ X sgbuf.sg_kill = CKILL; X ltbuf.t_werasc = CWERASE; X ltbuf.t_lnextc = CLNEXT; X ioctl(STDIN, TIOCGETP, &sgbuf); /* get and save editing characters */ X ioctl(STDIN, TIOCGLTC, <buf); X rubchar = sgbuf.sg_erase; X rubline = sgbuf.sg_kill; X rubword = ltbuf.t_werasc; X litchar = ltbuf.t_lnextc; X} X X X/* X * Read the next input character. If it is an editing character, X * abort the current context and longjmp back to the last setjmp. X * NOTE: for proper results, the caller should not alter the global X * state until the full command has been read in. This includes such X * things as prompting for input or saving values. Otherwise, improper X * results will occur if the user edits the command. X */ Xscanchar() X{ X register int ch; /* current character */ X Xloop: if (scanreadptr < scanwriteptr) /* get saved char if have any */ X return(*scanreadptr++); X ch = scanroutine() & 0x7f; /* get new character */ X if (ch == litchar) { /* literal input */ X ch = scanroutine() & 0x7f; X goto store; X } X if (ch == rubchar) { /* character erase */ X if (scanwriteptr <= scanbuffer) { X write(STDERR, "\007", 1); X goto loop; X } X scanwriteptr--; X scanreadptr = scanbuffer; X longjmp(scanjumpbuf, SCAN_EDIT); X } X if (ch == rubword) { /* word erase */ X if (scanwriteptr <= scanbuffer) goto loop; X while ((--scanwriteptr >= scanbuffer) && X ((*scanwriteptr == ' ') || (*scanwriteptr == '\t'))) ; X scanwriteptr++; X while ((--scanwriteptr >= scanbuffer) && X ((*scanwriteptr != ' ') && (*scanwriteptr != '\t'))) ; X scanwriteptr++; X scanreadptr = scanbuffer; X longjmp(scanjumpbuf, SCAN_EDIT); X } X if (ch == rubline) { /* line erase */ X if (scanwriteptr <= scanbuffer) goto loop; X scanwriteptr = scanbuffer; X scanreadptr = scanbuffer; X longjmp(scanjumpbuf, SCAN_EDIT); X } X Xstore: if (scanwriteptr >= scanbuffer + SCAN_SIZE) { X write(STDERR, "\007", 1); X goto loop; X } X *scanwriteptr++ = ch; X return(*scanreadptr++); X} X X X/* Abort reading of the current command */ Xscanabort() X{ X scanreadptr = scanbuffer; X scanwriteptr = scanbuffer; X longjmp(scanjumpbuf, SCAN_ABORT); X} X X X/* Indicate no more characters ready yet */ Xscaneof() X{ X scanreadptr = scanbuffer; X longjmp(scanjumpbuf, SCAN_EOF); X} X X X/* Simply reset input and output pointers without longjmping */ Xscanreset() X{ X scanreadptr = scanbuffer; X scanwriteptr = scanbuffer; X} //E*O*F scan.c// echo x - vars.c sed -e 's/^X//' > "vars.c" << '//E*O*F vars.c//' X#ifdef SCCS Xstatic char *sccsid = "@(#)vars.c 1.11 2/2/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X#endif X X#include "life.h" X Xenum vars { /* ordering of variables */ X v_minx, v_miny, v_maxx, v_maxy, v_cells, v_cx, v_cy, v_px, v_py, X v_vminx, v_vmaxx, v_vminy, v_vmaxy, v_vcells, v_vx, v_vy, X v_sminx, v_smaxx, v_sminy, v_smaxy, v_scells, v_gen, v_scale, X v_freq, v_born, v_died, v_endlist X}; X X X/* X * Table of multi-character variables. These are arranged in this table X * in rows of 3 so that you can see what the display will look like for X * the listvariables routine. X */ Xstruct vartab { X char *v_name; /* name of variable */ X enum vars v_type; /* variable id */ X} vartab[] = { X "cx", v_cx, "vx", v_vx, "px", v_px, X "cy", v_cy, "vy", v_vy, "py", v_py, X "minx", v_minx, "vminx", v_vminx, "sminx", v_sminx, X "maxx", v_maxx, "vmaxx", v_vmaxx, "smaxx", v_smaxx, X "maxy", v_maxy, "vmaxy", v_vmaxy, "smaxy", v_smaxy, X "miny", v_miny, "vminy", v_vminy, "sminy", v_sminy, X "cells", v_cells, "vcells", v_vcells, "scells", v_scells, X "gen", v_gen, "born", v_born, "died", v_died, X "scale", v_scale, "freq", v_freq, NULL, v_endlist X}; X X Xstatic char *curcp; /* current character to parse */ Xstatic long lowervars[26]; /* lower case single-char variable values */ Xstatic long uppervars[26]; /* upper case single-char variable values */ X X X/* X * Return the value of a multiple character variable name. This can be X * either a single character name, or else one of a fixed set of multi- X * character names. All variable names must start with either a letter X * or a dollar sign followed by a letter. X */ Xgetvariable(cp) X register char *cp; /* name of variable */ X{ X register struct vartab *vp; /* variable pointer */ X X if (*cp == '$') cp++; /* skip any dollar sign */ X if (cp[1] == '\0') { /* single character name */ X return(getvariable1(*cp)); X } X if (((*cp < 'a') || (*cp > 'z')) && ((*cp < 'A') || (*cp > 'Z'))) { X error("Bad variable name"); X } X for (vp = vartab; ; vp++) { /* find name in table */ X if (vp->v_name == NULL) error("Unknown variable"); X if (strcmp(vp->v_name, cp) == 0) break; X } X return(getvariablebytype(vp->v_type)); /* get value */ X} X X X/* X * Return the value of a variable given its enum value. X */ Xgetvariablebytype(type) X enum vars type; /* variable type to get */ X{ X register struct object *obj; /* current object */ X register long value; /* value to return */ X register long *ptr; /* pointer to minmax value */ X register long *sptr; /* another one */ X long sign; /* sign for result */ X long minrow, maxrow, mincol, maxcol; /* results of minmax */ X X obj = curobj; X value = 0; X sign = 1; X ptr = NULL; X sptr = NULL; X X switch (type) { /* collect value */ X case v_cx: value = obj->o_curcol; break; X case v_cy: value = -obj->o_currow; break; X case v_px: value = obj->o_pcol; break; X case v_py: value = -obj->o_prow; break; X case v_vx: value = (obj->o_mincol + obj->o_maxcol) / 2; break; X case v_vy: value = -(obj->o_minrow + obj->o_maxrow) / 2; break; X case v_vminx: value = obj->o_mincol; break; X case v_vmaxx: value = obj->o_maxcol; break; X case v_vminy: value = -obj->o_maxrow; break; X case v_vmaxy: value = -obj->o_minrow; break; X case v_vcells: value = markregion(obj, MARK_ANY, obj->o_minrow, X obj->o_maxrow, obj->o_mincol, obj->o_maxcol); X break; X case v_cells: value = obj->o_count; break; X case v_gen: value = obj->o_gen; break; X case v_born: value = obj->o_born; break; X case v_died: value = obj->o_died; break; X case v_freq: value = frequency; break; X case v_scale: value = obj->o_scale; break; X case v_minx: ptr = &mincol; break; X case v_maxx: ptr = &maxcol; break; X case v_miny: ptr = &maxrow; sign = -1; break; X case v_maxy: ptr = &minrow; sign = -1; break; X case v_scells: value = countmarks(obj, MARK_SEE); break; X case v_sminx: sptr = &mincol; break; X case v_smaxx: sptr = &maxcol; break; X case v_sminy: sptr = &maxrow; sign = -1; break; X case v_smaxy: sptr = &minrow; sign = -1; break; X } X /* X * Call proper minmax routines if we need to X */ X if (ptr && (minmax(obj, &minrow, &maxrow, &mincol, &maxcol) == 0)) X value = *ptr; X if (sptr&&(markminmax(obj,MARK_SEE,&minrow,&maxrow,&mincol,&maxcol)==0)) X value = *sptr; X return(sign * value); X} X X X/* X * Return the value of a single character variable name (a-z or A-Z). X */ Xgetvariable1(ch) X register int ch; /* variable character */ X{ X if ((ch >= 'a') && (ch <= 'z')) X return(lowervars[ch - 'a']); X if ((ch >= 'A') && (ch <= 'Z')) X return(lowervars[ch - 'A']); X error("Bad variable name"); X} X X X/* X * Set the value of a variable name. Multi-character names cannot be set. X */ Xsetvariable(cp, value) X register char *cp; /* name of variable to set */ X{ X if (*cp == '$') cp++; /* skip any dollar sign */ X if (cp[1] != '\0') { X error("Cannot set multi-character variables"); X } X setvariable1(*cp, value); /* do it */ X} X X X/* X * Set the value of a single-character variable (a-z or A-Z). X */ Xsetvariable1(ch, value) X register int ch; /* variable character */ X{ X if ((ch >= 'a') && (ch <= 'z')) { X lowervars[ch - 'a'] = value; X return; X } X if ((ch >= 'A') && (ch <= 'Z')) { X lowervars[ch - 'A'] = value; X return; X } X error("Bad variable name"); X} X X X/* X * Display the current values of the variables. Show all multi-character X * variable values, and those single character variables which have a X * nonzero value. The output is given three variables per line. X */ Xlistvariables() X{ X register struct vartab *vp; /* variable table pointer */ X register long *var; /* simple variable pointer */ X register int count; /* counter for formatting */ X X dpywindow(0, -1, 0, -1); X dpyprintf("Variable names and values:"); X count = 0; X for (vp = vartab; vp->v_name; vp++) { X dpyprintf("%s%s\t%d", (count++ % 3) ? "\t\t" : "\n", X vp->v_name, getvariablebytype(vp->v_type)); X } X count = 0; /* create blank line */ X for (var = uppervars; var < &uppervars[26]; var++) { X if (*var == 0) continue; X if (count == 0) dpychar('\n'); X dpyprintf("%s%c\t%d", (count++ % 3) ? "\t\t" : "\n", X 'A' + (var - uppervars), *var); X } X for (var = lowervars; var < &lowervars[26]; var++) { X if (*var == 0) continue; X if (count == 0) dpychar('\n'); X dpyprintf("%s%c\t%d", (count++ % 3) ? "\t\t" : "\n", X 'a' + (var - lowervars), *var); X } X dpyprintf("\n\nMany names starting with 'v' refer to visible cells\n"); X dpyprintf("Many names starting with 's' refer to selected cells\n"); X spacewait(); /* wait for space before clearing */ X} X X X/* X * Evaluate an expression and return its value. The expression can X * contain numbers, variables, the normal arithmetic operators, and X * parenthesized expressions. The usual precedence rules are used. X */ Xgetexpression(str) X register char *str; /* string to parse */ X{ X long value; /* resulting value */ X X while (*str == ' ') str++; X if (*str == '\0') error("Null expression"); X curcp = str; X value = parsesum(); X if (*curcp != '\0') error("Bad expression"); X return(value); X} X X X/* Parse the sum of products */ Xparsesum() X{ X register long value; /* value to return */ X X while (*curcp == ' ') curcp++; X switch (*curcp) { /* check for uninary operators */ X case '-': X curcp++; X value = -parseproduct(); X break; X case '+': X curcp++; X /* proceed into default case */ X default: X value = parseproduct(); X } X while (1) switch (*curcp++) { X case '+': /* sum of products */ X value += parseproduct(); X continue; X case '-': /* difference of products */ X value -= parseproduct(); X continue; X case ' ': /* space */ X continue; X default: /* end of sum */ X curcp--; X return(value); X } X} X X X/* Parse the product of terms */ Xparseproduct() X{ X register long value; /* value to return */ X register long value2; /* temporary value */ X X value = parseterm(); X while (1) switch (*curcp++) { X case '*': /* product of terms */ X value *= parseterm(); X continue; X case '/': /* division of terms */ X value2 = parseterm(); X if (value2 == 0) error("division by zero"); X value /= value2; X continue; X case '%': /* modulo of terms */ X value2 = parseterm(); X if (value2 == 0) error("division by zero"); X value %= value2; X continue; X case ' ': /* space */ X continue; X default: /* end of product */ X curcp--; X return(value); X } X} X X X/* Parse a single term */ Xparseterm() X{ X register long value; /* value to return */ X register int ch; /* current character */ X X while (*curcp == ' ') curcp++; X ch = *curcp; X if ((ch >= '0') && (ch <= '9')) { /* number */ X value = 0; X do X value = (value * 10) + *curcp++ - '0'; X while ((*curcp >= '0') && (*curcp <= '9')); X return(value); X } X if (ch == '(') { /* parenthesized expression */ X curcp++; X while (*curcp == ' ') curcp++; X if (*curcp == ')') error("Null expression"); X value = parsesum(); X while (*curcp == ' ') curcp++; X if (*curcp != ')') error("Unmatched parenthesis"); X *curcp++; X return(value); X } X if (ch == ')') error("Unmatched parenthesis"); X return(parsename()); X} X X X/* X * Parse a variable name and return its value. X */ Xparsename() X{ X register char *cp; /* current character */ X register long value; /* value of variable */ X char oldch; /* old character after name */ X X cp = curcp; X if (*cp == '$') cp++; X while (((*cp >= 'a') && (*cp <= 'z')) || X ((*cp >= 'A') && (*cp <= 'Z')) || X ((*cp >= '0') && (*cp <= '9'))) cp++; X oldch = *cp; X *cp = '\0'; X value = getvariable(curcp); X *cp = oldch; X curcp = cp; X return(value); X} //E*O*F vars.c// echo x - view.c sed -e 's/^X//' > "view.c" << '//E*O*F view.c//' X#ifdef SCCS Xstatic char *sccsid = "@(#)view.c 1.5 2/2/85"; Xstatic char *cpyrid = "@(#)Copyright (C) 1985 by D Bell"; X#endif X X#include "life.h" X X X/* X * Set the scaling factor for the specified object. This also centers X * the view around the current cursor location. X */ Xsetscale(obj, sf) X register struct object *obj; /* object to set scale of */ X register int sf; /* scaling factor */ X{ X if (sf <= 0) sf = 1; X if (sf > MAXSCALE) sf = MAXSCALE; X obj->o_scale = sf; X obj->o_minrow = obj->o_currow - (rowradius * sf) + (sf / 2); X obj->o_maxrow = obj->o_minrow + (2 * rowradius * sf); X obj->o_mincol = obj->o_curcol - (colradius * sf) + (sf / 2); X obj->o_maxcol = obj->o_mincol + (2 * colradius * sf); X if (obj == curobj) redraw = 1; /* update if object visible */ X} X X X/* X * Perform auto-scaling of the current object. This implies picking a X * scaling factor such that the whole object fits in the screen. The X * scale factor is never decreased. When the scale factor is large, X * convenient ones are picked. Returns the new scale factor. X */ Xautoscale() X{ X register struct object *obj; /* current object */ X register int sf; /* scaling factor */ X int minrow, maxrow, mincol, maxcol; /* limits of object */ X X obj = curobj; X minmax(obj, &minrow, &maxrow, &mincol, &maxcol); X sf = obj->o_scale; X if (mincol > maxcol) return(sf); X while ((sf <= MAXSCALE) && X ((minrow < obj->o_minrow) || (maxrow > obj->o_maxrow) || X (mincol < obj->o_mincol) || (maxcol > obj->o_maxcol))) { X sf++; X if (sf > 20) sf += (5 - (sf % 5)); X if (sf > 50) sf += (10 - (sf % 10)); X if (sf > 200) sf += (100 - (sf % 100)); X setscale(obj, sf); X } X return(obj->o_scale); X} X X X/* X * Position the view of the current object to show both the given region and X * the current cursor location. If this is impossible, just the cursor X * location will be positioned. X */ Xpositionview(minrow, maxrow, mincol, maxcol) X register long minrow, maxrow, mincol, maxcol; /* region to show */ X{ X register struct object *obj; /* current object */ X register int sf; /* current scale factor */ X X obj = curobj; X sf = obj->o_scale; X if (minrow > obj->o_currow) minrow = obj->o_currow; X if (maxrow < obj->o_currow) maxrow = obj->o_currow; X if (mincol > obj->o_curcol) mincol = obj->o_curcol; X if (maxcol < obj->o_curcol) maxcol = obj->o_curcol; X if ((maxrow - minrow) > (2 * sf * rowradius)) { /* too many rows */ X minrow = obj->o_currow; X maxrow = obj->o_currow; X } X if ((maxcol - mincol) > (2 * sf * colradius)) { /* too many columns */ X mincol = obj->o_curcol; X maxcol = obj->o_curcol; X } X if (minrow < obj->o_minrow) { X obj->o_minrow = minrow; X obj->o_maxrow = minrow + (rowradius * sf * 2) + sf - 1; X redraw = 1; X } X if (maxrow > obj->o_maxrow) { X obj->o_maxrow = maxrow; X obj->o_minrow = maxrow - (rowradius * sf * 2) + sf - 1; X redraw = 1; X } X if (mincol < obj->o_mincol) { X obj->o_mincol = mincol; X obj->o_maxcol = mincol + (colradius * sf * 2) + sf - 1; X redraw = 1; X } X if (maxcol > obj->o_maxcol) { X obj->o_maxcol = maxcol; X obj->o_mincol = maxcol - (colradius * sf * 2) + sf - 1; X redraw = 1; X } X} X X X/* X * Show the view around the current window location if the view has changed. X * The update flag indicates that the status line and cursor need updating. X * The redraw flag indicates that the view of the cells also needs updating. X */ Xupdateview() X{ X register struct object *obj; /* current object */ X X if ((interact | redraw | update) == 0) return; X obj = curobj; X positionview(obj->o_currow,obj->o_currow,obj->o_curcol,obj->o_curcol); X if (obj->o_autoscale) autoscale(); X if (redraw) { /* show visible cells */ X freqcount = frequency; X dpywindow(1, -1, 0, -1); X if (obj->o_scale <= 1) X viewnormal(); X else X viewscale(obj->o_scale); X dpyclrwindow(); X } X if (redraw || update) { /* show status and position cursor */ X viewstatus(); X dpywindow(1, -1, 0, -1); X dpymove((obj->o_currow - obj->o_minrow) / obj->o_scale, X (obj->o_curcol - obj->o_mincol) / obj->o_scale); X dpyupdate(); X } X update = 0; /* no more updates until prodded */ X redraw = 0; X interact = 0; X} X X X X/* X * Update the status line for the object. X */ Xviewstatus() X{ X register struct object *obj; /* current object */ X X dpywindow(0, 0, 0, -1); /* output in top line */ X if (errorstring) { /* show error string if present */ X dpystr(errorstring); X dpyclrline(); X return; X } X obj = curobj; X dpyprintf("Gen:%d cells:%d", obj->o_gen, obj->o_count); X if (obj->o_count > seecount) X dpyprintf("(%du)", obj->o_count - seecount); X if (obj->o_born) dpyprintf(" born:%d", obj->o_born); X if (obj->o_died) dpyprintf(" died:%d", obj->o_died); X if (frequency > 1) dpyprintf(" freq:%d", frequency); X if ((obj->o_scale > 1) || (obj->o_autoscale)) X dpyprintf(" %scale:%d", X (obj->o_autoscale ? "autos" : "s") , obj->o_scale); X if (strcmp(rulestring, "3,23")) dpyprintf(" rules:%s", rulestring); X if (obj->o_lock) dpystr(" locked"); X if (curinput > inputs) dpyprintf(" cmd-nest:%d", curinput - inputs); X switch (curinput->i_type) { X case INP_TTY: /* reading from terminal */ X if (curinput != inputs) dpystr(" tty-wait"); X break; X case INP_FILE: /* reading from file */ X dpystr(" cmd-file"); X break; X case INP_LOOP: /* reading from loop */ X if (curinput->i_macro) { X dpyprintf(" macro-define-%c",curinput->i_macro); X break; X } X dpyprintf(" loop%s (curval:%d end:%d)", X curinput->i_first ? "-define" : "", X curinput->i_curval, curinput->i_endval); X break; X case INP_MACRO: /* reading from macro */ X dpyprintf(" macro-%c", curinput->i_macro); X break; X } X if (mode == M_INSERT) dpystr(" inserting"); X if (mode == M_DELETE) dpystr(" deleting"); X if (curobj != mainobject) dpyprintf(" \"%s\"", curobj->o_name); X dpyclrwindow(); X} X X X/* Show the cells around the cursor normally (scale factor of 1) */ Xviewnormal() X{ X register struct row *rp; /* current row */ X register struct cell *cp; /* current cell */ X register int row; /* current row number */ X register int col; /* current column number */ X register char *str; /* characters for line */ X register char *endstr; /* end of characters for line */ X register struct object *obj; /* current object */ X X obj = curobj; X rp = obj->o_firstrow; X row = obj->o_minrow; X seecount = 0; X while (row > rp->r_row) rp = rp->r_next; X for (; row <= obj->o_maxrow; row++) { X if (row != rp->r_row) { /* blank row */ X if (gridchar == ' ') { X dpychar('\n'); X continue; X } X str = stringbuf; X for (col = obj->o_mincol; col <= obj->o_maxcol; col++) { X *str++ = gridchar; X } X *str++ = '\n'; X dpywrite(stringbuf, str - stringbuf); X continue; X } X str = stringbuf; X endstr = str; X cp = rp->r_firstcell; X col = obj->o_mincol; X while (col > cp->c_col) cp = cp->c_next; X for (; col <= obj->o_maxcol; col++) { X if (col != cp->c_col) { /* blank cell */ X *str++ = gridchar; X if (gridchar != ' ') endstr = str; X continue; X } X *str = '#'; X if ((cp->c_marks & MARK_SEE) == 0) *str = 'O'; X endstr = ++str; X seecount++; X cp = cp->c_next; X } X *endstr++ = '\n'; X dpywrite(stringbuf, endstr - stringbuf); X rp = rp->r_next; X } X} X X X/* X * Show the view around the cursor with an arbitrary scale factor. X * When in this mode, characters from 1 to 9 (or * if 10 or more) X * are used to indicate how many cells are in each n by n square. X */ Xviewscale(sf) X register int sf; /* scale factor */ X{ X register int row; /* current row number */ X register int col; /* current column number */ X register int sum; /* number of cells in square */ X register struct cell *cp; /* current cell structure */ X register struct object *obj; /* current object */ X struct cell **cpp; /* pointer into cell table */ X struct cell **endcpp; /* end of cell table */ X struct row *rp; /* row pointer */ X char *str; /* buffer pointer */ X char *endstr; /* end of buffer */ X struct cell *cptab[MAXSCALE]; /* table of rows */ X X obj = curobj; X row = obj->o_minrow; X col = obj->o_mincol; X endcpp = &cptab[sf]; X seecount = 0; X for (rp = curobj->o_firstrow; (rp->r_row < row); rp = rp->r_next) ; X while (row <= obj->o_maxrow) { X /* X * If there is a large gap to the next row number then X * the terminal line is empty. X */ X if (rp->r_row >= (row + sf)) { /* no rows here */ X if (gridchar == ' ') { X dpychar('\n'); X row += sf; X continue; X } X str = stringbuf; X for (col=obj->o_mincol; col<=obj->o_maxcol; col+=sf) { X *str++ = gridchar; X } X *str++ = '\n'; X dpywrite(stringbuf, str - stringbuf); X row += sf; X continue; X } X /* X * Collect the rows to be searched for one terminal line. X * Dummy up empty rows if necessary. X */ X for (cpp = cptab; cpp < endcpp; cpp++) { X *cpp = termcell; X if (rp->r_row > row++) continue; X *cpp = rp->r_firstcell; X rp = rp->r_next; X } X str = stringbuf; X endstr = str; X /* X * Advance along each row to the next range of columns, X * adding cells found to get the result for each square. X */ X for (col = obj->o_mincol; col <= obj->o_maxcol; col += sf) { X sum = 0; X for (cpp = cptab; cpp < endcpp; cpp++) { X cp = *cpp; X while (col > cp->c_col) cp = cp->c_next; X while ((col + sf) >= cp->c_col) { X sum++; X cp = cp->c_next; X } X *cpp = cp; X } X if (sum == 0) { /* no cells in square */ X *str++ = gridchar; X if (gridchar != ' ') endstr = str; X continue; X } X *str = '*'; /* show number of cells */ X if (sum <= 9) *str = '0' + sum; X endstr = ++str; X seecount += sum; X } X *endstr++ = '\n'; X dpywrite(stringbuf, endstr - stringbuf); X } X} //E*O*F view.c// echo done -- no comment is a comment.
chongo@nsc.UUCP (Landon Noll) (04/12/85)
In net.sources: I am reposting DBell's DPY sources in responce to all the folks who needed them for his LIFE and WAR progs. DBell is no longer on the net (in fact in a few days he will no longer be in this hemisphere) otherwise he would have done this himself. In net.sources.games: I am reposting DBell's LIFE sources complete with the changes for the new DPY. Both LIFE and WAR (which was posted by DBell not long ago) require the use of DPY so be sure and GRAB IT FROM net.sources NOW!!! ---------------------begin DBell's comments------------------------------- This article contains a collection of interesting life objects readable by the life program which I just posted. They should be put into the directory /usr/games/lib/life. Then the program can easily find them for anyone. Most of these objects were gotten from a couple of issues of the "Lifeline" magazine by Robert T. Wainwright, which existed around 1971. A few objects are gotten from Martin Gardner's book, "Wheels, Life, and other Amusements". And finally a few (very few) objects are mine. If you know of or find any other objects which are interesting, I would like to have them to add to the library. Thank you! #---Cut here and place in it's own directory, then feed to Bourne shell--- # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # This archive contains: # acorn.l (7898 chars) # bigpuff.l (6222 chars) # blinkership.l (1156 chars) # eatblinker.l (777 chars) # escorts.l (348 chars) # gun1.l (590 chars) # gun2.l (850 chars) # puffer.l (328 chars) # pulsars.l (1783 chars) # rake.l (697 chars) # relay.l (498 chars) # round.l (2856 chars) # rpent.l (2092 chars) # shipgun.l (2431 chars) # shuttles.l (889 chars) # switch.l (588 chars) # echo x - acorn.l sed -e 's/^X//' > "acorn.l" << '//E*O*F acorn.l//' X! "acorn" (cells 640 length 2497 width 2325 generation 5206) X! The cursor is positioned on the 7-bit starting pattern called the acorn. X! This pattern evolves into the configuration shown around it in 5206 moves. X1248k1126h@! X2302.OO X2302..OO X2302.O X173 X62..OO X62.OO X62...O X248 X2012..OO X2012.O.O X2012...O X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X1831.OOO X1831...O X1831..O X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X415..OO X415.OO X415...O X220 X656..O X656.OO X656.O.O X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X1399..O X1399..OO X1399.O.O X198 X1002..O X1002.OO X1002.O.O X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X1002.........................................................................OO X1002.........................................................................O.O X1002..........................................................................OO X. X. X. X. X. X. X. X. X1002................................................................OO X1002................................................................OO X. X. X. X. X. X. X1002....................................................................OO X1002....................................................................OO X. X. X1002.................................OOO X. X. X. X. X. X1010........................................................................O X1010.......................................................................O.O X1010.......................................................................O.O X1010...............................................................OO.......O X1010..................................................OO..........O..O X1010...............................OO.................OO...O.......OO X1010...............................OO.....................O.O X1010......................................................O.O X1010.......................................................O...........OO X1010..................OO...............................................O.O X1010.................O..O...............................................O X1010..................OO X. X. X1010...........................O X1010..OO......................O.O X1010.O..O.....................O..O X1011.OO.......................OO.................................................OOO X. X. X. X1011..............................................................................OO X1011......................................................O.......................OO X1011.....................................................O.O X1011....................................OO...............O.O X1011...................................O..O...............O X1011....................................OO X. X1011................................O.................O X1011...............................O.O................O X1041.O.O................O...............................OO X1041..O.................................................OO X. X. X1057......................................................................O X1057..O..................................................................O.O X1057.O.O....................OO............................................OO X1028.O............................O..O..................O..O......................OO X1027.O.O28.OO19.O.O23.OO8.3O X1027.O.O..................................................O X1028.O97.O X1033.O25.OO23.OO39.O.O X1032.O.O24.OO23.OO40.OO X1032.O.O X1033.O80.3O X1090................OO X1090.OO.............OO X1090.OO....................................................O X1090.......................................................O...................OO X1090.......................................................O...................OO X1042.OO81.O X1011.3O28.OO80.O.O X1125.OO.............................OO X1009.O5.O140.OO X1009.O5.O95.O X1009.O5.O41.O52.O.O X1056.O.O...................................................O.O X1011.3O43.OO52.O63.OO X1174.O.O X1174.OO X1045..O..........................................................................OO X1045.O.O...............OOO......................................................O..O X1045.O.O......................................O.................................O..O X1045..O......................................O.O......................OO.........OO X1045.........................................O..O.....................OO X1045..........................................O.O X1045..........OO........OO.....................O X1045..........OO........OO X1045............................OO....OO X1045............................OO....OO X. X1045........OO.......................................O X1045........OO......................................O.O X1026.O46.OO17.OO48.OO X1026.O46.OO67.OO X1026.O X. X. X1084.OO........OOO..................................................O X1015.3O17.O11.O35.O.O3.O57.O X1026.O7.O.O10.O36.O4.O57.O X1025.O.O.......OO..........O.........................................O X1025.O.O83.OO12.OO7.OO13.3O X1026.O84.OO6.OO3.O..O5.O..O X1043.O74.O..O3.OO7.OO X1021.OO7.OO10.O.O73.O..O X1020.O..O5.O..O9.O.O34.O39.OO9.O X1021.OO7.OO11.O34.O.O48.O.O X1078.O.O................................................O.O....................O X1026.O52.O50.O14.O5.O.O X1025.O.O117.O5.O.O X1025.O.O117.O6.O X1026.O X1039.O80.OO X1038.O.O79.OO X1039.O.O X1039..O X1045.O..........................................................................OO X1034.O10.O74.OO24.OO X1033.O.O9.O8.O69.O21.OO X1034.O.O17.O58.OO11.O X1035.O18.O58.OO8.OO..3O4.OO X1088.OO............................................OO X1050.OOO...OOO.............................OO..................OOO X1149.O X1054.O93.O.O X1054.O93.O.O X1054.O94.O X. X. X1056.O X1056.O.......OO X1056.O......O..O................................O X1064.OO9.OO16.OO4.O73.3O X1052.OOO...OOO..............OO................OO....O X1070.O79.O X1056.O12.O.O77.O.O X1056.O12.O.O77.O.O X1056.O13.O79.O X. X1089....................................OOO...........................OO X1089.................................................................O..O X1089..O...............................................................OO X1089.O.O X1089.O.O........................................................O X1089..O.........................................................O X1089...............................O............................O X1089...............................O X1089...............................O......................O.......OOO X1089..........................................OO..........O X1089.........................................O..O.........O.....O X1057.OO71.OO16.O X1056.O..O88.O X1048..........O.O..........OO............OO X1048...........O...........OO............OO X. X. X. X. X. X. X. X1048.OO.......................................OO X1048.OO......................................O..O X1048..........................................O.O X1048...........................................O X. X. X. X1048..............................................OOO X. X1048............................................O.....O X1048............................................O.....O X1048..........................O.................O.....O X1048.........................O.O X1048.........................O.O..................OOO X1048..........................O X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X1048....................................................OO X1048....................................................O.O X1048.....................................................OO X576 X1788.O X1788..OO X1788.OO X128 X326.O.O X326.OO X326..O X148 X1979.O X1979..OO X1979.OO X241 XO.O XOO X.O X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X2322...O X2322.O.O X2322..OO //E*O*F acorn.l// echo x - bigpuff.l sed -e 's/^X//' > "bigpuff.l" << '//E*O*F bigpuff.l//' X! "bigpuff" (cells 2115 length 556 width 1074 generation 0) X! This shows what a typical dirty puffer train is like. This object X! started with only the engine and spaceships at the front (the right). X! This is the result after it has settled down after about 1600 generations. X! Try examining it at a scale of 8 to see its structure. The random stuff X! at the left is the "startup" exhaust, before it established its period. X279k1073h@! XOO XO.O XO X48 X250.OO X249.OO X251.O X15 X304.OO X303.OO X305.O X15 X358.OO X357.OO X359.O X15 X412.OO X411.OO X413.O X15 X466.OO X465.OO X467.O X15 X520.OO X519.OO X521.O X15 X574.OO X573.OO X575.O X15 X628.OO X627.OO X629.O X15 X682.OO X681.OO X683.O X8 X375.OO X366.O8.OO X366.O X366.O X3 X736.OO X735.OO X344.O392.O X343.O.O X343.O.O X344.O27.O X372.O X339.OO7.OO22.O X338.O..O5.O..O484.OO X339.OO7.OO18.3O3.3O453.O.O.O X835.OO X344.O484.O3.5O X343.O.O487.O..O.O X343.O.O484.O5.O.O X344.O486.OO X826.O X788.3O34.O.O X789.O35.O.O..OO4.OO X789.O36.O5.O3.O.O X447.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO14.O.O17.OO21.OO5.O.O X446.O..O32.O..O32.O..O32.O..O32.O..O32.O..O32.O..O32.O..O32.O..O32.O..O13.O.O16.O..O28.OO X333.O113.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO29.O X332.O.O453.OO48.O.O X332.O.O452.OO38.OO10.OO X333.O78.OO373.3O37.OO10.O3.3O21.O X412.OO428.O..O21.O41.O X345.3O440.O.OO49.OO3.O19.3O40.OO X412.OO375.OO49.O6.O X412.OO428.O.O.O17.O5.O34.O4.O.O X765.O3.O75.O16.O9.O31.O5.O.O X765.O..OO71.OO18.OO4.O4.OO30.OO X349.O97.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO59.OO45.O16.OO4.3O4.OO34.3O X348.OO74.OO20.O..O4.OO26.O..O4.OO26.O..O4.OO26.O..O4.OO26.O..O4.OO26.O..O4.OO26.O..O4.OO26.O..O4.OO26.O..O4.OO21.OO.OO8.OO15.O..O3.O.O9.OO25.OO18.O..3O.3O..O5.OO25.O3.O4.OO X324.O23.OO73.O..O20.OO5.OO27.OO5.OO27.OO5.OO27.OO5.OO27.OO5.OO27.OO5.OO27.OO5.OO27.OO5.OO27.OO5.OO25.O8.OO15.O.O..3O.OO8.OO46.OO.O3.O.OO6.OO25.OO7.OO X323.O.O22.O75.OO335.OO5.OO26.O..O4.O29.O28.OO5.OO34.O3.O26.3O5.O X323.O.O434.O7.OO33.OO28.O.O71.OO.OO24.O3.O3.O.O.O.O X324.O29.OO44.O359.O7.O29.O3.OO12.OO14.O3.O74.O24.OO.OO..O..O3.O X353.O..O17.OO23.O.O13.O351.O30.OO33.O.OO68.O3.O32.OO X354.O.O16.O.O23.O.O12.O.O344.O3.OO31.OO35.O30.OO22.OO11.4O..O33.O.6O X355.O5.O11.OO25.O12.O..O345.O36.O35.O.O28.OO20.OO..O5.OO.O.OO3.O41.O X360.O.O51.OO383.4O31.OO.OO48.O..O.O4.O..O3.3O37.O..4O X359.O..O404.3O30.O..OO28.O52.OO5.O3.O..O5.O37.O3.O.O X360.OO37.OO400.O..O.O26.O.OO26.3O21.O..O..O.OO.O7.O28.O14.O26.OO X399.OO40.O5.O29.O5.O29.O5.O29.O5.O29.O5.O29.O5.O29.O5.O29.O5.O29.O5.O29.O5.O31.O3.OO26.O11.OO14.O..O21.3O..4O.O5.OO27.OO.OO11.O..O23.O..O X389.OO50.O5.O29.O5.O29.O5.O29.O5.O29.O5.O29.O5.O29.O5.O29.O5.O29.O5.O29.O5.O36.O24.O.O11.OO12.O4.O24.O.3O3.O4.O.O25.O3.OO7.3O4.O22.OO..O X364.OO23.OO50.O5.O29.O5.O29.O5.O29.O5.O29.O5.O29.O5.O29.O5.O29.O5.O29.O5.O29.O5.O32.O4.O23.OO7.O18.O.4O11.O18.3O14.O17.O5.O7.OO..3O23.O.O.OO X364.OO5.OO88.3O33.3O33.3O33.3O33.3O33.3O33.3O33.3O33.3O33.3O21.O11.3O17.O.O17.O.OO12.O.O33.O.O19.OO7.O29.O.O..OO.O X371.OO70.3O33.3O33.3O33.3O33.3O33.3O33.3O33.3O33.3O33.3O34.O3.O32.OO17.OO15.OO34.OO17.O.6O4.3O26.O..O4.3O X423.O41.O35.O35.O35.O35.O35.O35.O35.O35.O35.O15.3O17.O106.O.O3.O5.OO3.3O30.3O X423.O41.O35.O35.O35.O35.O35.O35.O35.O35.O35.O35.O107.3O9.O.3O22.3O6.O.O X328.OO61.OO12.O12.O4.O41.O35.O35.O35.O35.O35.O35.O35.O35.O35.O35.O107.O..OO12.O19.OO..O..O4.O X328.OO61.OO12.O11.O.O529.O15.OO3.O4.O.O X405.O12.OO512.O11.O4.O15.O.O.O..O X932.5O.4O23.3O..O5.O.O X939.OO5.O19.O8.O.OO X316.OO620.O6.O19.O4.O6.O33.O46.O..O X316.OO614.O.7O3.O21.O9.O..O31.O50.O X415.O514.O6.OO.O.OO28.4O..3O29.O.O45.O3.O X414.O.O50.O35.O35.O35.O35.O35.O35.O35.O35.O35.O35.O35.O35.O36.3O32.O5.O30.O.O.O6.OO38.4O X414.O..O48.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O29.O3.OO3.3O28.O.O4.O.O29.OO.O6.OO X366.OO47.OO13.3O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O30.3O36.O.O..O.O30.3O40.OO X342.OO22.OO99.O35.O35.O35.O35.O35.O35.O35.O35.O35.O35.O35.O35.O71.O4.O32.O3.O8.O28.O..OO13.O..O X342.OO3.OO79.O5.O573.OO11.O.O21.O10.O16.O X330.OO.OO11.O..O41.O26.3O7.O5.O573.O.O9.O22.OO3.O20.O3.O X330.OO.OO12.OO41.O.O35.O5.O19.O35.O35.O35.O35.O35.O35.O35.O35.O35.O35.O35.O35.O35.O86.O9.OO..O18.O4.OO4.O16.4O X391.OO60.O.O9.3O21.O.O9.3O21.O.O9.3O21.O.O9.3O21.O.O9.3O21.O.O9.3O21.O.O9.3O21.O.O9.3O21.O.O9.3O21.O.O9.3O21.O.O9.3O21.O.O9.3O21.O.O9.3O21.O.O9.3O36.O47.OO..O16.O3.O3.O3.O7.OO..3O X430.3O20.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O34.OO11.O.O20.OO16.O.O25.O3.O3.O4.O3.O3.OO..3O X454.O35.O35.O35.O35.O35.O35.O35.O35.O35.O35.O35.O35.O35.O35.O.O10.O.O10.O9.O.O30.O12.O..O5.O3.O3.O3.OO..3O X366.O491.OO34.OO34.OO27.O.O7.4O10.O.O9.O.O10.OO..O.O11.O3.O9.O7.OO..4O14.4O X365.O.O490.OO34.OO34.OO28.O8.3O11.O.O10.O16.O6.3O..OO3.O11.OO.OO.O3.3O.O12.O3.O X365.OO601.O.O13.O24.O10.OO3.OO.O.O11.6O13.OO10.O X336.OO105.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO60.O..O13.OO.O14.3O13.4O5.O..O X336.OO75.OO28.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO61.OO8.6O..O31.OO.OO X413.OO559.OO42.O3.OO4.O33.OO X974.OO42.O4.O X864.OO157.3O X864.O.O49.4O97.O.O.O..O X864.O51.OO..O30.OO34.OO28.O5.O X916.O.OO31.OO34.OO30.3O X442.OO575.3O X372.OO34.OO32.OO36.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO X372.OO34.OO70.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO X4 X987.O X987.OO X986.O.O X4 X328.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO12.OO20.OO34.OO X328.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO12.O.O19.OO34.OO X918.O X937.O X936.O..O X935.OO41.OO X934.O5.O37.O..O X930.12O36.O..O X324.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO26.OO.O.OO..O40.OO.OO X324.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO24.OO3.O..3O42.OO.OO X926.3O.OO.3O42.OO3.OO X931.O..O15.3O25.O..3O X932.OO23.OO19.O X950.O6.O.O19.O.O X951.O4.O..O20.O X947.O10.O X948.O X1 X334.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO X333.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O.O33.O X333.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO34.OO35.O X946.O X225 X34.O X34.O.O X34.OO //E*O*F bigpuff.l// echo x - blinkership.l sed -e 's/^X//' > "blinkership.l" << '//E*O*F blinkership.l//' X! "blinkership" (cells 72 length 17 width 30 generation 0) X! This is a puffer train which leaves no permanent exhaust, and yet grows X! arbitrarily large. The engine and spaceships to the right of the blinker X! produce a new blinker every 12 generations. Without the object to the left X! of the blinker (a predecessor to traffic lights), this puffer train would X! just leave behind an infinite line of blinkers. However, the object reacts X! with the blinkers with a period of 26, and totally destroys them. The speed X! of the reaction is 6/13 the speed of light, which is slightly slower than the X! rest of the spaceship. Therefore, every 156 generations the puffer train X! grows in size by one blinker. X! X8k29h@! X...............O..O X...................O X...............O...O X................OOOO......OO X.........................OOOO X.........................OO.OO X................OOO.OO.....OO XOOO....O.......OO.....O XO.O....O......O.......OO XOOO....O.......OO.....O X................OOO.OO.....OO X.........................OO.OO X.........................OOOO X................OOOO......OO X...............O...O X...................O X...............O..O //E*O*F blinkership.l// echo x - eatblinker.l sed -e 's/^X//' > "eatblinker.l" << '//E*O*F eatblinker.l//' X! "eatblinker" (cells 54 length 21 width 51 generation 0) X! This shows a reaction in which a set of traffic lights is tugged around by X! a string of blinkers. The traffic lights eats each blinker it encounters, X! leaving no debris. X10k26h@! X................OOO X. X..............O.....O..........O...............O X..............O.....O.OOO......O......OOO......O X..............O.....O..........O...............O XOOO X................OOO X. X.................................................O X.................................................O X.................................................O X. X.O X.O X.O X. X. X................................................OOO X...O...............O...............O X...O......OOO......O......OOO......O......OOO X...O...............O...............O //E*O*F eatblinker.l// echo x - escorts.l sed -e 's/^X//' > "escorts.l" << '//E*O*F escorts.l//' X! "escorts" (cells 57 length 16 width 15 generation 0) X! This shows the largest overweight spaceship which can be safely escorted X! by only two other spaceships. X8k14h@! X.......OOOO X......OOOOOO X......OOOO.OO X..........OO X. X..OO XO............O X..............O XO.............O X.OOOOOOOOOOOOOO X. X. X.......OOOO X......OOOOOO X......OOOO.OO X..........OO //E*O*F escorts.l// echo x - gun1.l sed -e 's/^X//' > "gun1.l" << '//E*O*F gun1.l//' X! "gun1" (cells 44 length 12 width 36 generation 30) X! This is the original glider gun, which creates a glider every 30 generations. X! This was the first object found which grew without bound. It is composed of X! two identical but phase-shifted shuttle elements which collide properly to X! produce a glider. X11k10h@! X............O X............O.O XOO...........O.O.......O XOO...........O..O......OO X.............O.O..OO....OO X............O.O...OO....OOO.......OO X............O.....OO....OO........OO X.......................OO X.......................O X...........O X..........O X..........OOO //E*O*F gun1.l// echo x - gun2.l sed -e 's/^X//' > "gun2.l" << '//E*O*F gun2.l//' X! "gun2" (cells 61 length 34 width 41 generation 46) X! This is the "newgun", which creates a glider every 46 generations. It is X! composed of two "busy bee" shuttles which are positioned at right angles X! to each other, and which collide properly to produce a glider. The cursor X! is positioned at the approximate location where each glider is created. X! One glider is shown having already been generated. X10k7h@! X.......................O X............OO........OO...............OO X............OO.......OO................OO X.O....................OO..OO XOO XO.O X. X......................OO..OO X.....................OO................OO X......................OO...............OO X.......................O X. X. X. X. X.......OOO...OOO X.......O..O.O..O X.......O.......O X. X........O.....O X.........OO.OO X. X. X. X. X. X. X. X. X. X. X. X.......OO.....OO X.......OO.....OO //E*O*F gun2.l// echo x - puffer.l sed -e 's/^X//' > "puffer.l" << '//E*O*F puffer.l//' X! "puffer" (cells 43 length 17 width 14 generation 0) X! This is one of the simplest puffer trains. It generates a beehive every X! 12 generations. X8k13h@! X.OOOO XO...O X....O XO..O X..........OOOO X.........O...O X.............O X.....OO..O..O X.....OOO X.....OO..O..O X.............O X.........O...O X..........OOOO XO..O X....O XO...O X.OOOO //E*O*F puffer.l// echo x - pulsars.l sed -e 's/^X//' > "pulsars.l" << '//E*O*F pulsars.l//' X! "pulsars" (cells 237 length 23 width 75 generation 0) X! This is a collection of well known (and not so well known) oscillators. X! Their periods are 2, 3, 4, 5, 6, 8, 14, and 15. I let you figure out X! which is which! X11k39h@! X...O...........OO X..O.O..........OO..............................OOO...OOO.............OO X......................................................................O X.OO...O........O.............................O....O.O....O.......OOO..OO X.O.....O......O.O..............O....O........O....O.O....O.....OOO.O...O X....O.O.......O..O..OO.......OO.OOOO.OO......O....O.O....O.....O....O.OO X...OO.............O.OO.........O....O..........OOO...OOO...........O.O X................OO...............................................OO.O....O X...............................................OOO...OOO.........O...O.OOO X.............................................O....O.O....O.......OO..OOO XOO...........O...............................O....O.O....O........O XOO.........O.O.......OOO.....................O....O.O....O........OO X..OO........O.O.......OOO X..OO........O..................................OOO...OOO X.............................................................O X.......O...........................OOO.....................OOO.......O..O X.......O.O.........................OOO....................O..........O..O X.....O.............OO...OO.........OOO...........O........OO.......OO.OO.OO X..........OO.......O.O.O.O............OOO........O...................O..O X....OO.............O.O.O.O............OOO........O......OO...........O..O X..........O..........O.O..............OOO................O.........OO.OO.OO X......O.O...........OO.OO.............................OOO............O..O X........O...........OO.OO.............................O..............O..O //E*O*F pulsars.l// echo x - rake.l sed -e 's/^X//' > "rake.l" << '//E*O*F rake.l//' X! "rake" (cells 63 length 19 width 26 generation 20) X! This is the "space rake", which is a puffer train which generates a X! dense stream of forward-traveling gliders. A new glider is generated X! every 20 generations. The debris near the rearward spaceship is X! transformed into a glider. X9k23h@! X...............OO.....OOOO X.............OO.OO...O...O X.............OOOO........O X..............OO.....O..O X. X....................O X...............O...O..O X..............OO.......O X...............OO......O X................OO....OO XO.O..................O XO..O..............OO X. X.OOO X......................OOOO X....O..O.............O...O X........O................O X....O...O............O..O X.....OOOO //E*O*F rake.l// echo x - relay.l sed -e 's/^X//' > "relay.l" << '//E*O*F relay.l//' X! "relay" (cells 29 length 7 width 35 generation 0) X! This shows a glider which travels back and forth between two pentadecathlons. X! The distance between the endpoints can be made arbitrarily large. This is X! one of three known "shuttles", but is isn't as useful as the others (which X! form the engines of the two kinds of glider guns). X4k17h@! X..O....O XOO.OOOO.OO X..O....O X................O X.................OO........O....O X................OO.......OO.OOOO.OO X...........................O....O //E*O*F relay.l// echo x - round.l sed -e 's/^X//' > "round.l" << '//E*O*F round.l//' X! "round" (cells 200 length 72 width 72 generation 0) X! This shows four busy bee shuttles arranged so as to keep a collection X! or gliders traveling constantly between them. Each glider is turned 90 X! degrees by a busy bee. The glider near the cursor is about to be turned. X9k39h@! X......................................O X......................................OO X.........................OO.........O.OOO...........OO X.........................OO.............OO..........OO X.......................................OO X.......................................O X. X.......................................O X.......................................OO X.........................OO.............OO..........OO X.........................OO.........O.OOO...........OO X......................................OO X......................................O X.................................OO X..................................OO X.................................O X. X. X..OO.....OO X..OO.....OO X. X.............................................O X..............................................OO X.............................................OO X......................O X......................OO.....................................OO.....OO X.....................O.O.....................................OO.....OO X. X. X. X...O.....O X..OOO...OOO X.OO.OO.OO.OO XOOO.......OOO...........................................O.O X.........................................................OO X..O.......O..............................................O X..............O..............................................O.......O X.............OO X.............O.O...........................................OOO.......OOO X............................................................OO.OO.OO.OO X.............................................................OOO...OOO X..............................................................O.....O X. X. X. X..OO.....OO.....................................O.O X..OO.....OO.....................................OO X.................................................O X.........................OO X........................OO X..........................O X. X.............................................................OO.....OO X.............................................................OO.....OO X. X. X......................................O X....................................OO X.....................................OO X.................................O X................................OO X..................OO...........OOO.O.........OO X..................OO..........OO.............OO X...............................OO X................................O X. X................................O X...............................OO X..................OO..........OO.............OO X..................OO...........OOO.O.........OO X................................OO X.................................O //E*O*F round.l// echo x - rpent.l sed -e 's/^X//' > "rpent.l" << '//E*O*F rpent.l//' X! "rpent" (cells 121 length 501 width 525 generation 1103) X! This is the result of the infamous R-pentomino. The cursor is positioned X! on the starting pattern. This object takes 1103 generations to stabilize. X241k265h@! X492.OO X492..OO X492.O X177 X373.OOO X373...O X373..O X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X233...............................OO X233...............................O.O X233................................OO X. X. X. X. X. X. X. X. X233......................OO X233......................OO X. X. X. X. X. X. X233..........................OO X233..........................OO X. X. X. X. X. X. X. X. X. X. X. X233.............................OO X233............................O..O X233.............................OO...............O X233.............................................O.O X233.............................................OO X. X. X233..........................OO X233.........................O..O....O...........OO X233...................O......OO....OOO..........OO X233...................O..............O X233...................O X. X. X. X. X. X233.OO X233.OO X. X. X. X. X. X. X. X. X. X. X. X. X. X233.....OO...........................................O X233....O..O.........................................O.O X233.....OO.........................................O..O X233.................................................OO X. X233.....................OO X233.....................OO X233.........................................O X233.........................................O X233.........................................O X. X. X. X. X. X. X. X. X. X233...........OO X233...........OO.............................O X233..........................................O X233..........................................O X. X. X. X. X. X. X233......................................OO X233......................................OO X233........O.........OO X233........O.........OO X233........O X. X. X. X. X. X. X. X. X233............OO X233...........O..O X233............OO X145 XO.O XOO X.O X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X.......................O.O X.......................OO X........................O X. X. X522..O X522...O X522.OOO X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X. X14.O X14.O.O X14.OO //E*O*F rpent.l// echo x - shipgun.l sed -e 's/^X//' > "shipgun.l" << '//E*O*F shipgun.l//' X! "shipgun" (cells 241 length 72 width 98 generation 0) X! This is a middle-weight spaceship factory. It is by constructed by placing X! three newguns (period 46) together such that the three gliders collide to X! produce a middleweight spaceship every 46 turns. The cursor is positioned X! near the gliders which are about to collide, and a previously built spaceship X! is to the right. The smaller period 30 glider guns cannot be used in place X! of the period 46 guns since there is not enough space for the spaceship to X! escape before the next gliders appear. X33k47h@! X.........................OO.....OO X.........................OO.....OO X. X. X. X. X. X. X. X. X. X. X........................OOO.....OOO X.........................OOO...OOO X.......................O.OOOO.OOOO.O X........................OO..O.O..OO X.........................OOO...OOO X..........................O.....O X. X. X. X. X. X............O...O XOO.........O.....O XOO...............O X............O...OO X.............OOO.......................O........................O X.....................................O.O......................O...O X.............OOO......................OO...........................O X............O...OO.......................OO....O..............O....O XOO...............O.........OO...........O.O...OO...............OOOOO XOO.........O.....O.........OO.............O...O.O X............O...O X. X. X. X.............................OO X............OO...............O.O X12.OO15.O.OO36.OO9.OO14.OO X30.OO.....................................OO........OO...............OO X30.O.................................................OOOOO X30.............................OO.....................OOOO X30.O............OO............OO X30.OO...........OO..............O.....................OOOO X12.OO...............O.OO...............................................OOOOO X12.OO15.O.O47.OO15.OO X29.OO.................................................OO..............OO X. X. X. X29........OOO...OOO X29........O..O.O..O X29.......O...O.O...O X29.......OOOO...OOOO..................OOO...OOO X29........O.......O..................O..O...O..O X29.......................................O.O X29.......................................O.O X29.......................................O.O X29...................................O..O...O..O X29....................................O.......O X. X. X. X. X. X. X. X. X. X29........OO.....OO...................OO.....OO X29........OO.....OO...................OO.....OO //E*O*F shipgun.l// echo x - shuttles.l sed -e 's/^X//' > "shuttles.l" << '//E*O*F shuttles.l//' X! "shuttles" (cells 49 length 11 width 62 generation 0) X! These are two of the three known shuttles (the third is "relay" in which X! a glider travels back and forth between two pentadecathalons). These are X! the "engines" which power the two kinds of glider guns. The shuttle on the X! left has a period of 30. The shuttle on the right has a period of 46. X5k29h@! X..................................................O X.................................OO...............OO........OO X.........OO......................OO................OO.......OO X.........O..O.....................................OO X.............O XOO...........O......OO XOO...........O......OO X.........O..O.....................................OO X.........OO......................OO................OO.......OO X.................................OO...............OO........OO X..................................................O //E*O*F shuttles.l// echo x - switch.l sed -e 's/^X//' > "switch.l" << '//E*O*F switch.l//' X! "switch" (cells 11 length 7 width 28 generation 0) X! This is the incredible switch engine. This puffer train travels diagonally X! at 1/12 the speed of light towards the upper left with a period of 96. X! It's only exhaust is many sets of blocks arranged in a zig-zag. This is the X! smallest known object which grows without bound (11 cells). The block to X! the right is necessary to stabilize the exhaust. Without it, the engine is X! killed by the untamed exhaust before it reaches 12 full periods. X1k@! X.O.O XO X.O..O X...OOO X. X..........................OO X..........................O //E*O*F switch.l// echo done -- no comment is a comment.