Many of our rules are copies or variations of rules to be found in the 1987 book, Cellular Automata Machines, by Tommaso Toffoli and Norman Margolus. For each of our rules, the table below gives the most closely related rules from [Margolus&Toffoli87], with page number. Where our rule is identical to the one from Margolus & Toffoli, we put an equals sign.
JC Name | Same? | Margolus & Toffoli Related Rules | Page | |
---|---|---|---|---|
Aurora | ||||
Axons | ||||
Balloons | ||||
Bob | ||||
Border | = | Border/Hollow | 113 | |
BraiLife | ||||
Brain | = | Brian's Brain | 47 | |
Dendrite, DenTim | Naive-Diffusion, Dendrite | 84, 168 | ||
EcoLiBra | ||||
Faders | ||||
Flick | ||||
Fractal | = | Me-Neither | 132 | |
FredMem | Parity (w/7 bit Echo) | 31 | ||
Gyre | ||||
Heat, HeatWave | ||||
HGlass | = | Hglass | 29 | |
Hodge | ||||
Langton | ||||
Life | = | Life with Echo | 23 | |
Parks | ||||
PerfumeT | = | TM-Gas/Walls | 160 | |
PerfumeX | HPP-Gas (w/Wall) | 123 | ||
Pond | TM Gas, Circular wave | 131, 172 | ||
RainZha | ||||
Ranch | ||||
RevEcoli | ||||
Rug, RugF, RugLap | ||||
ShortPi | ||||
Soot | TM-Gas, Dendrite | 131, 168 | ||
SoundCa | ||||
Sublime | = | 2D-Brownian | 156 | |
TimeTun | = | Time-Tunnel | 52 | |
Venus | ||||
Vote | Anneal (w/1 bit Echo) | 41 | ||
VoteDNA | ||||
XTC | HPP-Gas & TM-Gas | 123, 131 | ||
Zhabo, Zhabof, Zhaboff | = | Tube-Worms | 83 |
Now I'm going to say a little about each of these rules, taking them in alphabetical order.
The rule for Aurora is a one-dimensional version of the RC Rug rule: EveryCell's new state is taken to be one greater than the neighborhood average of L, C, and R. That is, we have
When a color value reaches 16, it is rounded back down to 0. The effect produced is like globby paint running down the screen, though really you are seeing "one-dimensional gliders" moving back and forth and interacting. Programs defining the Aurora rule are available in the Pascal, C, and BASIC languages.
I called the rule Aurora because when I visited Norman Packard's
office at the Institute for Advanced Study in 1985, he showed me a
smoother (eight visible neighbor bits and 255 colors) version of this
rule and remarked that it looked like the northern lights.
Aurora is one of five predefined 1D rules we include with JC, the other being Axons, Parks, ShortPi, and SoundCa.
L+C+R | 3 | 2 | 1 | 0 |
NewState | 0 | 0 | 1 | 0 |
---|
These rules are spoken of as having a "totalistic Wolfram Code" which is the integer gotten by regarding the four bits put into the table as the four bits of a binary integer. The table as illustrated holds the bits 0010, which is of course binary for the number 2. So the illustrated rule has totalistic Wolfram code number 2. (See [Wolfram86] for details.)
The next level of generality is to look at one-dimensional CA rules of only two states whose new state is determined by the LCR contents of the cell and its two neighbors. At this next level of generality, we pay attention to the positions of the bits. There are 256 distinct rules of this type. The 256 rules are gotten by the 256 different ways of filling eight bits into the eight spaces in the second line of a tiny lookup table:
LCR | 111 | 110 | 101 | 100 | 011 | 010 | 001 | 000 |
NewState | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
---|
These rules have a "Wolfram Code" which is the integer gotten by regarding the eight bits put into the table as an eight bit binary integer. The table illustrated holds the bits 00010110 which is binary for 22. So the rule has Wolfram code number 22.
As it turns out, rule 22 is the same as totalistic rule 2: in each case a cell's new state is 1 if and only if there is exactly one firing bit among L, C, and R. But of course many rules are not equivalent to totalistic rules.
The rule Axons which we show is this same rule #22 (or totalistic rule #2)... with one extra feature. Axons is the reversible version of rule 22.
The trick for making Axons "reversible" is given in detail in my discussion of the Fractal rule below. For now, suffice it to note that, once Axons is running, you can press the letters O and S to see the rule start running "backwards."
I named this rule Axons after the long nerve fibers known as axons. These are up to several feet long, and are coated in a fatty sheath that pinches in every now and then. The Axons rule grows long fibers that are swathed in pinchy sausage casing, just like the cells. The fibers are continuous precisely because this rule is reversible. The existence of a firing cell, or of a hole in the cells, can't be forgotten (unless you bump into a mask cell). So the fibers bounce and tangle, but they never just stop.
In order that some complexity accumulates, a reversible rule needs some input, so I provide for mask cells which periodically pulse cells on. The existence of these periodic masks can of course interfere with perfect reversibility. In the case of Axons, the mask cells get covered over rather soon.
For the sake of elegance, I could have written Axons as a totalistic rule with code 2. "Wow, that's pretty! What's the program?" "Binary 10. The number two. I wonder if I can patent it." Actually I wrote Axons as a general LCR Wolfram rule so that you can try putting different WolfCodes in, recompiling the rule, and running the variants.
If you use WolfCode 178 you get a really neat rule which I call Bamboo.
PROGRAM Axons; {A one dimensional rule that only looks at one bit of two neighbors. We run it as WorldType 3, which gets one bit from each of 8 neighbors. The rule is totalistic, meaning that it only looks at the sum of its neighborhood. The rule is also reversible, meaning that it saves its past state and XORs its calculated new state with the past state. A final touch to make this rule look good is that I use my extra six bits of state as a five bit clock and as a mask indicator. Whenever the clock counts up to 31, I turn on the bits where mask is on. The start pattern for this consists of two dots with bit #0 turned on, all the times set to 0, and a pair of dots with mask set to 1. You can vary the constant WolfCode to get other pictures.} USES JCMake; {$F+} { Required for function argument to genrule. } FUNCTION JCRule(OldState,LLLL,LLL,LL,L,Self, R,RR,RRR,RRRR:integer):integer; {This world type has variables for a cell and its eight nearest neighbors. This rule actually only uses L,C, and R, but we write it for the other variables so that this program can be used as a template for WorldType 2 & 3 programs that do use eight one-bit neighbors.} CONST WolfCode=22; VAR Sum,PastSelf,NewSelf,NewState,Time,Mask:integer; BEGIN Sum:=(4*L+2*Self+R); PastSelf:=(OldState SHR 1) AND 1; NewSelf:=(WolfCode SHR Sum) AND 1; NewState:=(Self SHL 1) OR (NewSelf XOR PastSelf); Time:=(OldState SHR 2) AND 31; Mask:=(OldState SHR 7) AND 1; IF Time=31 THEN JCRule:=(Mask SHL 7) OR (NewState) OR Mask ELSE JCRule:=(Mask SHL 7) OR ((Time+1)SHL 2) OR (NewState) END; BEGIN {Main} WorldType := 3; {World type: 8 neighbor ring} PalReq:= 'Mask3'; {Only show low two bits} PatReq:= 'Axons'; GenRule(JCRule); END.
Balloons is driven by Silverman's Brain rule. If enough firing Brain
cells are together, they turn on a permanent firing cell. These
permanent firing cells serve as seeds around which more turned-on
cells agglutinate. If a turned on cell is entirely surrounded, it
changes state, so that one soon gets the effect of cells with
membranes. As a final fillip, if there is too much excitement at a
cell's membrane, the membrane bursts and the cell goes over to a
"dead" state which can slowly be nibbled away by the ever active
Brain rule.
In order to make the JC version of Balloons look as much as possible like a zoomed-back version of the RC rule, the program uses a colorpalette RC.JCC which, when used on a VGA monitor, imitates the sixteen textmode colors of RC.
PROGRAM Balloons; {This realizes one of the RC ruletables. Any other RC ruletable can be implemented by making a copy of this program and changing the entries in the table below. } USES JCmake; {$F+} FUNCTION JCRule(Oldstate,NW,N,NE,W,Self,E,SW,S,SE:integer):integer; CONST RuleTable: ARRAY[0..143] OF integer= ( {EightSum} {0 1 2 3 4 5 6 7 8} {State} {0} 0, 0, 15, 0, 0, 0, 5, 0, 0, {1} 0, 0, 0, 0, 0, 0, 0, 0, 0, {2} 0, 0, 0, 0, 0, 0, 0, 0, 0, {3} 0, 0, 0, 0, 0, 0, 0, 0, 0, {4} 4, 4, 8, 4, 4, 4, 4, 4, 4, {5} 5, 5, 5, 5, 5, 7, 7, 9, 11, {6} 2, 2, 2, 2, 2, 2, 2, 2, 2, {7} 5, 5, 5, 5, 5, 13, 13, 9, 11, {8} 8, 8, 10, 8, 8, 8, 8, 8, 8, {9} 2, 2, 2, 2, 2, 9, 13, 9, 11, {10} 10, 10, 0, 10, 10, 10, 10, 10, 10, {11} 14, 14, 14, 14, 14, 14, 14, 14, 11, {12} 12, 12, 4, 12, 12, 12, 12, 12, 12, {13} 6, 6, 6, 6, 13, 13, 13, 9, 11, {14} 14, 14, 14, 12, 14, 14, 14, 14, 14, {15} 2, 2, 2, 2, 2, 2, 2, 2, 2 ); VAR EightSum,Index:integer; BEGIN {Function} OldState:=OldState AND 15; EightSum:=NW+N+NE+E+SE+S+SW+W; Index:=9*OldState + EightSum; JCRule:=RuleTable[Index] END; {Function} BEGIN {Main} {The RC palette reproduces the textmode state colors of RC} PalReq:='RC'; GenRule(JCRule) END. {Main}
The Bob rule's standard startup is with the Bob pattern, using the
subtle grayscale Bob palette that incorporates touches of yellow and
red. The Bob pattern is a picture of "Bob®", the chief religious
icon of the radical mockery scorn religion called
The Church of the
SubGenius.
"Bob" looks like the typical 1950s cartoon Dad. As the
Bob rule dissolves and reforms "Bob"'s visage, he goes through a
remarkable series of image transformations, demonstrating the terrific
power of cellular automata for creative image processing.
It's also fun, by the way, to feed the "Bob" pattern to a straight averaging rule by loading *Laplace. Laplace turns "Bob" into a urine-stained tabloid newspaper photo of a "face on Mars."
If you start the Bob rule from a random screen, it will take three or four hundred generations until you start seeing circular centers of activity, like bacterial cultures in a petri dish. After a thousand generations these centers have taken over. If you then cut out a black hole with the screen editor, the plaguey culture will flicker around the hole like flames. The flame illusion is enhanced if you use the Default colorpalette.
PROGRAM Bob; {This is modeled on the Hodgepodge rule of Gerhardt and Schuster, and can produce mild Zhabotinsky reactions. The start pattern used is the Shroud of Turing visage of "Bob". "Bob" is the High Epopt of the Church of the SubGenius. For more information about "Bob" and the Church, send $1 and a long stamped self-addressed envelope to: The SubGenius Foundation Box 140306 Dallas, TX 75214 The image of "Bob" is a registered trademark of the Church of the Subgenius and is used by special arrangement with Douglas St. Claire Smith, a.k.a. Ivan Stang. Inquiries about further usage of "Bob"'s image should be directed to Mr. Smith c/o The SubGenius Foundation.} USES JCmake; {$F+} { Required for function argument to genrule. } FUNCTION JCRule(Oldstate,NW,N,NE,W,Self, E,SW,S,SE:integer):integer; {The odd states are thought of as infected states. A cell in state zero enters an infected state if it has any infected neighbors. An infected cell becomes more infected until its state exceeds 129, at which time it drops back to 0.} VAR EightSum,Sickness,NewState:integer; BEGIN {Function} EightSum:=NW+N+NE+E+SE+S+SW+W; IF OldState=0 THEN IF EightSum=0 THEN NewState:=0 ELSE NewState:=EightSum OR 1 ELSE BEGIN Sickness:=OldState SHR 1; IF Sickness=64 THEN NewState:=0 ELSE BEGIN Sickness:=Sickness+EightSum+3; IF Sickness>64 THEN Sickness:=64; NewState:=(Sickness SHL 1) OR 1 END END; JCRule:=NewState END; {Function} BEGIN {Main} PatReq:='Bob'; PalReq:='Bob'; GenRule(JCRule) END. {Main}
The effect of Border is that lines keep getting thick, splitting into two, having the new pieces get thick and split to make four, and so on. Many of the Border patterns are reminiscent of the mathematical objects called "Cantor sets."
Border starts from the pattern Square, but it could equally well start
from a single dot. The rule begins to get exciting when the expanding
square wave from the center wraps around the screen edges and begins
to interfere with itself. First the pattern wraps top and bottom, and
then it wraps right and left; unlike CAM-6's
256×256 screen, our
screen is a rectangle.
A good way to watch the interference patterns evolve is to use the arrow keys to pan the screen until the interference region is in screen center. This means that one fourth of the original square pattern is at each screen corner.
It is interesting to note that no matter how intricate the pattern gets, it is still the deterministic outcome of the simple Border rule starting on a single Square or Dot.
The Border.PAS file looks like this:
PROGRAM Border; {This rule alternates between two cycles: In cycle 0, every cell touching a firing cell is turned on. In cycle 1, any cell which is the center of a block of 9 firing cells is turned off. Bit #0 is the firing bit and bit #7 is the cycle bit.} USES JCmake; {$F+} FUNCTION JCRule(Oldstate,NW,N,NE,W,Self,E,SW,S,SE: integer):integer; VAR NineSum,Cycle,NewCycle,NewSelf:integer; BEGIN NineSum:=NW+N+NE+W+Self+E+SW+S+SE; Cycle:=OldState SHR 7; {Shift down the high bit} NewCycle:=Cycle XOR 1; {Change 0 to 1 and 1 to 0} CASE Cycle OF 0: IF NineSum>0 THEN NewSelf:=1 ELSE NewSelf:=0; {Flood} 1: IF NineSum=9 THEN NewSelf:=0 ELSE NewSelf:=Self; {Hollow} END; JCRule:=(NewCycle SHL 7) OR NewSelf; END; BEGIN PalReq:='Mask1'; {This colorpalette only looks at bit #0} PatReq:='Square'; GenRule(JCRule); END.
The JC BraiLife rule after 213 generations. A hauler is about to hit a butterfly just above and to the right of the center of the diamond shape.
When I first started hacking cellular automata on the CAM-6 in 1987, I couldn't quite see how to think of a completely new rule. So I decided a good way to start might be to try combining some of the old rules, particularly the rules Life and Brain.
Life is very interesting, but it tends to die out. Brain, on the other hand, is extremely hard to kill off; if anything, Brain is too persistent. So I thought I might try running Life and Brain in parallel, using Brain to stimulate Life, and using Life to dampen Brain.
At first I had every firing Brain cell turn on a Life cell, and had every firing Life cell turn off a Brain cell, but, run fullscreen, this reaction quickly wipes Brain out. You can see the fullscreen reaction by loading BraiLife, clearing all the screens, setting plane 4 to 1, and randomizing plane 2. The keystrokes are as follows. Note that you do not press Enter after answering the "Initialize planes" prompts called up by pressing I:
l
brailife
i
a
0
i
4
1
i
2
r
F1
Enter
Instead of letting Brain and Life interact across the whole screen, I set up the BraiLife start pattern as a disk-shaped mask in plane #4 and two firing Brain bits in plane #2. This is what you see if you load BraiLife and let it run unaltered.
Note how Brain grows an oriental-carpet-patterned diamond from a start of two adjacent firing blocks. Where this diamond sweeps across the limb of the central disk, Life cells are turned on within the disk. Some of the life manages to boil out into the black region outside the disk.
Graphically, the development of BraiLife makes me think of a UFO that hovers near the atmosphere of a fallow planet (these are the starting Brain dots). The UFO sets off an energy blast, and the shock wave of the blast sweeps across the planet like the EMP-spike from an H-bomb. But instead of being destructive, the UFO energy turns on living cells in the planetary sea. Some of these cells manage to crawl out and flap around in the planetary atmosphere. The UFO energy pulse breaks into spacecruising creatures who are usually poisoned if they try to return to the planet they seeded.
All this from a disk, two dots, and a few lines of code!
The way in which BraiLife runs two parallel rules is to cycle between doing one and the other. Only the bit in plane #0 is visible to neighbors, so each cell alternates between showing its firing Life bit in #0 and showing its firing Brain bit in #0.
Here is BraiLife.PAS:
PROGRAM BraiLife; {This rule runs Life and Brain in parallel and lets them interact only within a certain masked region. In this region, firing Brain cells turn on Life cells, and firing Life cells keep Brain cells from turning on.} USES JCmake; {$F+} { Required for function argument to genrule. } FUNCTION JCRule(Oldstate,NW,N,NE,W,Self,E,SW,S,SE: integer):integer; {We use the eight bits of state as follows: Bit #0 is used to show either the Brain or the Life bit to neighbors; Bit #1 is the Life bit, Bit #2 is the firing Brain bit, Bit #3 is the refractory Brain bit, Bit #4 is the mask bit,and Bit #7 is the cycle bit.} VAR L,NewL,B,NewB,BR,NewBR,Mask,Cycle,NewCycle, EightSum: integer; BEGIN Cycle:=(OldState SHR 7) AND 1; { We AND with 1 because we only} Mask:= (OldState SHR 4) AND 1; { want to extract one bit of info.} BR:=(OldState SHR 3) AND 1; B:=(OldState SHR 2) AND 1; L:=(OldState SHR 1) AND 1; EightSum:=NW+N+NE+E+SE+S+SW+W; IF Cycle=0 THEN {This is the update Life cycle} BEGIN {The Life rule} IF (EightSum=3)OR((EightSum=2)AND(L=1)) THEN NewL:=1 ELSE NewL:=0; {Turned on by firing Brain cells within region of mask} IF (Mask=1)AND(B=1) THEN NewL:=1; NewCycle:=1; JCRule:=(NewCycle SHL 7) OR (Mask SHL 4) OR (BR SHL 3) OR (B SHL 2) OR (NewL SHL 1) OR B END; IF Cycle=1 THEN {This is the update Brain cycle} BEGIN {The Brain rule} IF ((BR=0)AND(B=0))AND(EightSum=2)THEN NewB:=1 ELSE NewB:=0; {Turned off by firing Life cells within region of mask} IF (L=1)AND(Mask=1) THEN NewB:=0; NewBR:=B; NewCycle:=0; JCRule:=(NewCycle SHL 7) OR (Mask SHL 4) OR (NewBR SHL 3) OR (NewB SHL 2) OR (L SHL 1) OR L END END; BEGIN {Main program} {The BraiLife.JCC colorpalette looks at bits 4,3,2, & 1. I got my colorpalette by disabling planes 0,5,6,7 of the Default.JCC and saving it as BraiLife.JCC} PalReq:='BraiLife'; {The starting BraiLife pattern has all bit 7s set to 0 (for synchronized cycles), has two adjacent cells of plane #2 turned on to start Brain, and has a big disk mask in plane #4.} PatReq:='BraiLife'; GenRule(JCRule); END.
To see the Butterfly Gun, get the pattern BFLYGUN.JCP.
My Butterfly Gun starts out with an extra east-moving hauler whose
purpose is to knock out a west-moving hauler which the Gun spits out
before getting into its standard operation. I once saw a much smaller
butterfly gun while playing with RC; I think it only used three
outriggers. If you find a small butterfly gun, let me know and we'll
put it in the next edition of the manual, if there is one.
The JC Dendrite rule. The white "gas" particles "freeze" to the red teapot shape, forming dendrites.
These rules both consist of a drifting "gas" pattern and a "frozen" seed pattern. The gas alternates between cycles of a) diffusing, and b) freezing if it is touching a frozen cell. The freezing process produces branching little dendrites of frozen cells. This phenomenon is a rough model for the physico-chemical process by which so-called accretion fractals are formed.
Dendrite shows a random gas and a frozen teapot; and DenTim shows a Tim-shaped gas and a frozen Autodesk logo.
The programs for the rules are the same except that Dendrite requests an random initial gas and DenTim requests a Tim-shaped initial gas. The programs store the gas bit in plane #7 and the frozen-cell bits in plane #6. The cycle bit is in plane #5. If the cycle bit is 0, we update the gas diffusion; and if the cycle bit is 1, we update the freezing. The visible bit is always the bit in plane #0; as we change cycles we alternate between showing the gas bit or the freeze bit. Depending whether we are updating Diffuse or Freeze, bit #0 is showing the gas bit or the freeze bit. The rule starts up in cycle 0, so we start it up with some visible gas bits in plane #0. The Dendrite.JCC colorpalette we use simply ignores all bits except #6 and #7.
The two rules use a cheap, imperfect method of mimicking gas diffusion. The trick is that at each gas update, each cell copies the gas value of one of its eight neighbors, the exact neighbor to be chosen at random. This is imperfect because it may happen that an individual firing gas particle may be copied by two or more of its neighbors (in which case one particle is splitting into several) or, just as bad, it may happen that an individual firing gas particle is copied by none of its neighbors (in which case a particle disappears). For a really good gas model, we would expect to have conservation of particles.
A gas with particle conservation can in fact be constructed (see the rules Sublime, PerfumeX, and PerfumeT), and we can indeed use these gases to grow dendrites as well (see Soot).
Why do the frozen cells of the Den... rules form those branching dendrites? The reason is so simple as nearly to evade comprehension: it is much easier for a randomly jostling gas particle to bump into one of the dendrite's tips or "capes" than it is for the gas particle to find its way up into one of the indentations or "estuaries."
This rule is a cross between Life and Brain. The basic idea is that the cells are divided between dark "sea" cells and light "land" cells. We run Brain in the sea, and on land we run not Life but AntiLife. All the land cells are normally firing cells, and the presence of an active AntiLife cell is signaled by having a land cell which is not firing. Full details on EcoLiBra are in §
The name EcoLiBra suggests 1) an ecology of Life and Brain, 2) a balanced situation (equilibrium), and 3) the human intestinal bacteria Escherichia coli, known as E. coli for short. The third connection is perhaps a bit unsavory, but remember that E. coli cells are in fact the favorite "guinea pigs" for present day genesplicing experiments. As one of the goals of CelLab is to promote the development of artificial life, the designer gene connection is entirely appropriate. I've given EcoLiBra a nice, symmetric start pattern, but it also does fine if you press R to randomize the screen. You can make a randomized screen a little more interesting by using the screen editor to drill a big black hole in the center. This can be done by using the following keystrokes
l
EcoLiBra Enter
Enter
r
e
0 (the numeral zero)
x
Seven presses of the Right Arrow key
Seven presses of the Up Arrow key
d
e
Enter
Three presses of the Left Arrow key
Three presses of the Down Arrow key
The last arrow key presses are simply to pan the screen so that the hole is in the center. The hole will grow.
Here is the Pascal code for EcoLiBra.PAS:
PROGRAM EcoLiBra; {This rule runs Brain in the sea and AntiLife on land. Six or seven firing Brain cells turn a sea cell into land. Seven "antifiring" Antilife cells turn a land cell into sea.} USES JCmake; {$F+} { Required for function argument to genrule. } FUNCTION JCRule(Oldstate,NW,N,NE,W,Self,E,SW,S,SE: integer):integer; {Here rather than thinking of bits, we think of state numbers. State 0 is dead sea State 1 is firing brain in sea State 2 is refractory brain in sea State 3 is dead land State 4 is firing life on land} VAR EightSum,NewState:integer; BEGIN {Function} EightSum:=NW+N+NE+E+SE+S+SW+W; IF odd(OldState) THEN NewState:=3 ELSE NewState:=0; IF OldState=0 THEN CASE EightSum OF 2: NewState:=1; 6,7: NewState:=3; ELSE NewState:=0; END; IF OldState=1 THEN NewState:=2; IF OldState=2 THEN NewState:=0; IF OldState=3 THEN CASE EightSum OF 5: NewState:=4; 1: NewState:=0; ELSE NewState:=3; END; IF OldState=4 THEN CASE EightSum OF 5,6: NewState:=4; ELSE NewState:=3 END; JCRule:=NewState END; {Function} BEGIN {Main} PalReq:='Default'; PatReq:='EcoLiBra'; GenRule(JCRule) END. {Main}
In Faders I perform a "genetic" cross between Life and Brain by
describing a rule which has threshold, persistence, and memory. A dead
Faders cell requires exactly 2 firing neighbors to get turned on. A
firing Faders cell keeps firing if it has exactly 2 firing neighbors.
And when a Faders cell leaves the firing state it goes into a sequence
of refractory states. Instead of having just 1 refractory state (like
Brain), the JC Faders has 127 refractory states.
Note that the version of Faders implemented in RC has only 7 refractory states. Faders with any desired number n of refractory states can be seen by entering the name N(n2222) when JC asks for the name of a rule to load. This works because Faders is an "NLUKY rule" as described in the Theory chapter. JC is designed to automatically generate and load NLUKY ruletables. For any positive integers n,l,u,k,y with l,u,k,y less than 10, entering N( nluky) with the integer names run together will cause JC to show the appropriate "NLUKY rule".
JC faders looks really great in VGA, but is also pretty interesting in CGA. The white cells are the firing cells. When Faders has a clear screen, it grows rapidly, leaving slowly dissolving trails behind. What keeps it coming back is that it can lay down "eggs" or "seeds" of activity. These eggs take the form of three adjacent firing cells configured into a small right-angle L-shape. You might call them fader eggs. Each cell in one of these threecell fader eggs has exactly two firing neighbors, so they persist until the refractory color veils dissolve and they can start turning on dead neighbors.
Faders looks good if you start it on our Billbord ad pattern. You can start it on any other pattern; even on a random screen. If you do start Faders on a random screen, it will look like nothing is happening for awhile. But just wait. When you randomize, you fill most of the screen with refractory states, but usually there will be some of those angle-iron eggs lurking in the haze, and as soon as it clears away they'll start spreading order.
Actually one of the best ways to start Faders is from a simple three block L or angle-iron of state 1 blocks. This pattern is stored as Faderegg.JCP. For a little practice with the editor, you can create this pattern with the screen editor and run Faders on it by using the following keystrokes.
l
faders Enter
F1
i a 0
e
1
Ins
End
UpArrow
Ins
LeftArrow
Ins
e
Enter
The pattern seems to run endlessly and evolves in interestingly different ways according to whether you have JC in the plane nowrap mode or in the torus wrap mode. If you want to run it in nowrap mode, press F3 before starting. The edges of the refractory faders patterns have an interesting fractal quality. The rule keeps laying down fader eggs that reseed the center. If you ran Faders from a single three-cell egg in an endless plane, I wonder how soon the pattern within some bounded N×N central region would repeat. For that matter, I wonder how soon it repeats on our screen? If you find out, please write.
We have Faders set to run with a special Faders colorpalette, but other colorpalettes can give good results. The colorpalette called AutoCAD.JCC looks particularly good.
Here is a program for Faders. The program is actually designed to generate the lookup table for any NLUKY rule, according to how the CONST variables are set.
PROGRAM Faders; {NLUKY 127 2 2 2 2} USES JCmake; {$F+} { Required for function argument to genrule. } FUNCTION JCRule(Oldstate,NW,N,NE,W, Self,E,SW,S,SE:integer):integer; CONST RN=127; L=2; U=2; K=2; Y=2; VAR EightSum,NewState:integer; BEGIN {Function} EightSum:=NW+N+NE+E+SE+S+SW+W; NewState:=0; IF (OldState=0) AND (L<=EightSum) AND (EightSum<=U) THEN NewState:=1; IF (OldState=1) THEN IF (K<=EightSum) AND (EightSum<=Y) THEN NewState:=1 ELSE NewState:=2; IF NOT(odd(OldState)) AND (0<OldState)AND(OldState<2*RN) THEN NewState:=OldState+2; JCRule:=NewState END; {Function} BEGIN {Main} {The Faders colorpalette shows state 0 as black, state 1 as white, and steps the refractory states through the spectrum.} PalReq:='Faders'; GenRule(JCRule) END. {Main}
PROGRAM Flick; { Flickercladding Interior Decoration Conceived by Rudy Rucker Drawn by Gary Wells Modeled with AutoCAD Rendered by AutoShade Perpetrated by Kelvin R. Throop. In this rule, we only change the cells whose high bits are on. These cells are updated according to the TimeTun rule.} USES JCMake; {$F+} FUNCTION JCRule(OldState,NW,N,NE,W,Self E,SW,S,SE:integer):integer; VAR Interest, OldSelf,NewSelf,FiveSum: integer; Fixed: Boolean; BEGIN {Cell is Fixed if high bit is off} Fixed := ((OldState SHR7)=0); If Fixed THEN JCRule:=OldState ELSE BEGIN OldSelf:=(OldState SHR 1) AND 1; FiveSum:=N+E+Self+S+W; IF (FiveSum=0) OR (FiveSum=5) THEN Interest:=0 ELSE Interest :=1; NewSelf:=Interest XOR OldSelf; JCRule:=128 OR (Self SHL 1) OR NewSelf; END END; BEGIN {Main} PalReq:='OpenPlan'; PatReq:='OpenPlan'; GenRule(JCRule); END.
Fractal is a "reversible" rule which means that if at any time you press O and then S to swap the contents of plane #0 with plane #1, Fractal will run backwards, returning to its start pattern and proceeding onward into negative time.
Fractal is reversible because the rule for Fractal can be written this
way:
where Parity is 1 if NE+NW+SE+SW is odd and 0 if NE+NW+SE+SW is even. The key thing about the equation just given is that, using the rules of algebra, we are allowed to swap NewSelf and OldSelf and get the equally valid equation:
This means that the rule for passing from new to old is the same as the rule for passing from old to new. Let's see why this means that pressing O and S makes Fractal run backwards.
If it is now time T, and plane #1 holds my screen at time T-1, then applying Fractal will: i) compute the screen for time T+1 and put this in plane #0, ii) meanwhile moving the old time T info from plane #0 into plane #1, and then iii) showing planes #0 and #1 on the screen.
Suppose that I now press O and S and swap the info in planes #0 and #1. Now the time T info is in plane #0 and the time T+1 info is in plane #1. The fractal rule computes the parity of each time T cell's neighborhood and subtracts off plane #1 value. But because we pressed O and S, the plane #1 value is the cell value for time T+1. Therefore the equation
applies, and the value we compute is indeed OldSelf, the value at time T-1! So now time T-1 values are put in plane #0 and time T values are saved in plane #1. The next application of the Fractal rule calculates the values for time T-2, and so on.
PROGRAM Fractal; {Based on Me-Neither rule, [Margolus&Toffoli87],p.132} USES JCmake; {$F+} { Required for function argument to genrule. } FUNCTION JCRule(Oldstate,NW,N,NE,W,Self,E, SW,S,SE:integer):integer; VAR Bit1,FourSum,Parity, NewSelf : integer; BEGIN {Function} Bit1:=(OldState SHR 1) AND 1; {Get the memory bit from plane #1} FourSum:=NE+NW+SE+SW; {Sum up the low bits of your 4 present neighbors} IF odd(FourSum) THEN Parity:=1 ELSE Parity:=0; {Set Parity from FourSum} NewSelf:= Parity XOR Bit1; {A XOR Bit1 is same as (A-Bit1) MOD 2} {Store present self in plane #1} JCRule:=(Self SHL 1) OR NewSelf; END; {Function} BEGIN {Main} PatReq:='Square'; GenRule(JCRule) END. {Main}
To make this rule a little more dramatic to look at, we use the extra seven planes as memory planes, so that plane #1 remembers plane #0's last pattern, plane #2 remembers the pattern before that, and so on.
It's fun to use the editor to draw some simple pattern in plane #0 and watch what Fredmem does with it.
We've written the rule for a nine-cell neighborhood. It works equally
well for a five-cell (N+E+W+S+Self) neighborhood, or even for a
three-cell (say N+Self+E) neighborhood. The threecell version with
only one bit of echo looks neat if you start with a square in one of
the screen's corners; you get things that look like hypercubes.
PROGRAM FredMem; {The Fredkin parity rule with the seven extra bits used as memory} USES JCmake; {$F+} { Required for function argument to genrule. } FUNCTION JCRule(Oldstate,NW,N,NE,W,Self, E,SW,S,SE:integer):integer; VAR NineSum,NewSelf,NewState:integer; BEGIN {Function} NineSum:=NW+N+NE+E+SE+S+SW+W+Self; IF odd(NineSum) THEN NewSelf:=1 ELSE NewSelf:=0; NewState:=(OldState SHL 1); {Shift memory bits to left} NewState:=NewState OR NewSelf; JCRule:=NewState END; {Function} BEGIN {Main} PatReq:='Square'; GenRule(JCRule) END. {Main}
The idea behind Gyre is that we load an initial pattern into
the plane so that cells can tell which of the four quadrants they are
in. In each quadrant, the cells pass their plane #0 bits around
according to a scheme which produces a circling motion around the
origin. The interest of the pattern arises because if I start out with
a block of firing cells in one quadrant, the block will "refract" as
it passes through the quadrant boundaries. Cells which are closer to
the origin get to the boundary before the more distant cells do, and
they pull increasingly ahead, drawing the original start pattern into
a spiral or "gyre."
PROGRAM Gyre; {Rule suggested by William Gosper. We lay down a mask marking the cartesian plane's four quadrants (Qs for short) by the numbers 0-3 in the arrangement 2 0 3 1 And we tell Q0 cells to copy SE, Q1 copy SW, Q2 copy NE, Q3 copy NW. A block of cell stuff will refract.} USES JCmake; {$F+} { Required for function argument to genrule. } FUNCTION JCRule(OldState,NW,N,NE,W,Self, E,SW,S,SE:integer):integer; {Bit #3 is the Barrier bit (for screen edge) Bits #2 and #1 are the Quadrant bits (00,01,10,or 11) Bit #0 is the firing "matter" bit.} VAR Barrier,Quadrant,NewSelf:integer; BEGIN {Function} Barrier:=(OldState SHR 3) AND 1; Quadrant:=(OldState SHR 1) AND 3; {Barrier cells stay barrier cells} IF Barrier=1 THEN JCRule:=8 ELSE BEGIN CASE Quadrant OF 0: NewSelf:=SE; 1: NewSelf:=SW; 2: NewSelf:=NE; 3: NewSelf:=NW; END; JCRule:=(Quadrant SHL 1) OR NewSelf; END; END; {Function} BEGIN {Main} WorldType:=0; {No wrap} PalReq:='Gyre'; {Colorful} {The pattern has Qs marked in bits 1,2 and a rectangle of food cells in Q0. Pattern also has a frame of Barrier cells around the screen edge.} PatReq:='Gyre'; GenRule(JCRule) END. {Main}
The special feature of Heat and Heatwave is that some of the cells are kept at fixed values. In particular, if a cell's low bit is on, the cell is not updated, the cell is simply kept at a fixed value. Specifying the cell's fixed value is a bit tricky because WorldType 10 only gives you five bits of the cell's state. As my purpose in writing Heat was to simulate heatflow between two objects of different temperatures, what I do is to suppose that the odd states with low five bits 1-15 are fixed at the low values 1 to 15; and that the odd states with low five bits 17-31 are fixed at the high values 128+17 to 128+31. Relative to a continuous modular ring of 256 eight-bit values, 128 is as far as you can get from zero.
The Heat pattern includes a large assemblage drawn in state 128+31, as
well as a leaning block in state 1. The "hot" assemblage sends out
waves of high state, but the "cool" block seems to do nothing. In
order to see both blocks in action, energize the background by putting
in some random mid-temperature gas. These keystrokes will work:
l
heat Enter
p
heat Enter
i 6 r
Enter
If anything, Heat works too well, converging very quickly to a boring equilibrium state. In HeatWave we keep cycling the colors of the non-fixed cells. Ultimately this leads to turbulent chaos in the nonfixed regions. HeatWave looks really gorgeous when run with the pattern StarTrek.JCP, which is an AutoCAD line drawing of the starship Enterprise.
Pascal code defining the Heat rule is presented in the Rule Definition chapter.
PROGRAM HGlass; {A rule from Margolus and Toffoli.} USES JCmake; {$F+} { Required for function argument to genrule. } FUNCTION JCRule(Oldstate,NW,N,NE,W,Self, E,SW,S,SE:integer):integer; VAR EWSNC:integer; BEGIN {Function} EWSNC:=16*E+8*W+4*S+2*N+Self; JCRule:=0; CASE EWSNC OF 1,2,3,11,21,25,29,30,31: JCRule:=1; END; END; {Function} BEGIN {Main} GenRule(JCRule) END. {Main}
The Hodgepodge rule is formulated in terms of two constants g and n:
These three conditions are 1) ragged start, 2) Laplacian spread, 3) synchronizing cutoff.
Below is the Pascal code for Hodge. The JC WorldType 10 is tailormade for averaging neighbors. In this WorldType we are only allowed to see five bits of EveryCell's OldState, so the cutoff value n has to be the largest number expressible in five bits: 31. An increment g value of 5 seems to work best here.
Hodge is a lovely rule which converges very rapidly. It looks nice with the colorpalettes Default, AutoCAD, and Ranch. Particularly with AutoCAD color, the patterns look extremely organic, suggesting successive microtomed cross-sections of a human brain.
Suppose you push the microtome concept and begin thinking of Hodge as generating a three dimensional stack of planes--just as a one dimensional rule generates a two dimensional spacetime sheet of stacked lines. When you look at Hodge (or at other Zhabotinsky reactions) you are seeing very striking three dimensional structures; things like paired vortex sheets in the surface of a river below a dam, the scroll pair stretching all the way down to the river bottom...to a fortuitous inhomogeneity in Hodge's random start.
Another thought: In three dimensions, a Zhabotinsky reaction would be like two paired nautilus shells, facing each other with their lips blending. The successive layers of such a growing pattern would build up a shape very like...a fetus!
Hodge is also interesting if you give it a bilaterally symmetric start; this leads to patterns that remind me of fanciful chinese lions with popeyes and twinscroll nostrils. A good bilaterally symmetric start can be gotten by loading the Rug.JCP pattern in nowrap mode and scrolling part of the pattern off the top of the screen. This leaves the left/right symmetry but breaks the up/down symmetry. This must be done before loading Hodge, because WorldType 10 rules like Hodge do not admit a nowrap mode. Once the pattern is set, feed it to Hodge. As the pattern settles in, try fooling with different colorpalette selections. Eventually you will get a living pattern so neat you want to save it. This is a good time to use the "save experiment" control: just press Ctrl-F3 and then enter the name you want to give it, say "lion."
ca
F3
p
rug Enter
Enter
UpArrow UpArrow UpArrow UpArrow
l
hodge Enter
Enter
...(time passes and you try colorpalettes)...
Ctrl-F3
lion
PROGRAM Hodge; USES JCmake; {$F+} { Required for function argument to genrule. } FUNCTION JCRule(FiveBits,Sum, a,b,c,d,e,f,g,h:integer):integer; VAR Temp:integer; BEGIN {Function} IF (FiveBits=0) THEN IF Sum<5 THEN Temp:=0 ELSE IF Sum<100 THEN Temp:=2 ELSE Temp:=3; IF (FiveBits>0) AND (FiveBits<31) THEN Temp:=((Sum SHR 3)+5)AND 255; IF Temp>=31 THEN Temp:=31; IF FiveBits=31 THEN Temp:=0; JCRule:=Temp END; {Function} BEGIN {Main} WorldType:=10; GenRule(JCRule) END. {Main}
00 | Was off and is off | Blank |
01 | Was off and is now on | Newborn |
10 | Was on and is now off | Newly dead |
11 | Was on and is now on | Established |
This particular shading enables the eye to easily pick out the regions
of greatest activity. If you would prefer to see vanilla, untinted
Life, load the colorpalette Mask1, which colors all odd
states white and all even states black.
Two Life patterns interesting to load are RPent and GlidrGun. See the Theory chapter for much info about Life.
Can one dimensional cellular automata carry out universal computation?
Yes, if we allow the cells to have many (about a hundred) different
states. But can it be done with only two states, as in Life?
Parks is a totalistic one-dimensional, two-state, six-neighbor CA rule
that is thought to be promising. As reported in [Dewdney88], p. 143,
James K. Park found a bidirectional "glider" gun for this rule
which shoots moving patterns out to the right and to the left.
PROGRAM Parks; {A totalistic one dimensional rule that sums one bit of three neighbors on either side plus a bit of self. Sum ranges from 0 to 7.} USES JCMake; {$F+} { Required for function argument to genrule. } FUNCTION JCRule(OldState,LLLL,LLL,LL,L,Self, R,RR,RRR,RRRR:integer):integer; CONST WolfCode=88; {Codes 0 through 127 are meaningful here} VAR Sum,PastSelf,NewSelf,NewState,Time,Mask:integer; BEGIN Sum:=(LLL+LL+L+Self+R+RR+RRR); JCRule:=(WolfCode SHR Sum) AND 1; END; BEGIN {Main} WorldType := 3; { World type: 8 neighbor ring } PalReq:= 'Mask1'; {The Parks pattern is 1111111111011. Pattern spews gliders left & right.} PatReq:= 'Parks'; GenRule(JCRule); END.
Our JC demos show four kinds of lattice gas in all. There is the "naive diffusion" lattice gas of Dendrite and DenTim. There is the "Brownian" lattice gas of Sublime. And there are the two Margolus and Toffoli gasses I call Xgas and Tgas. PerfumeX shows Xgas. PerfumeT, Pond, and Soot all show Tgas. The rule XTC shows Xgas and Tgas at the same time.
The main difference between Xgas and Tgas is that Xgas particles move along the screen's diagonals and Tgas particle move horizontally or vertically along the screen's main axes. In both gasses, the particles bounce off each other and off of the barrier cells we call "walls". Tgas bounces cleanly off the walls; Xgas does an odd little loop inside a wall when it bounces.
In both Perfume rules we start with two AutoCAD-drawn
perfume bottles, one open and one loosely stoppered. Each bottle
holds a cloud of gas. At startup, the gasses simply try to move along
the four directions that are natural to them. But then they run into
the perfume-bottles' walls, bounce off, and begin bouncing off each
other. Sooner or later the particles find their way out of the
bottles and into the "room."
An important feature to note about the Perfume rules is that no external randomization is being used. The gas particles disperse in clouds, but these clouds are strictly a deterministic result of the bouncings induced by the irregular shapes of the perfume bottles' walls. The gasses are, if you will, self-randomizing.
The particular trick by which the gas motions are achieved was developed by Norman Margolus, and is explained in the Theory chapter. As these rule programs are somewhat lengthy, I won't print them here. Instead I'll just list how the bits are used by the rules:
Bit #0 | is the machine visible bit for update |
Bit #1 | is used for the gas |
Bit #2 | is the wall |
Bit #3 | is the touch wall in my neighborhood bit |
Bits #4 & #5 | hold a position number between 0 and 3 |
Bit #6 | controls the check wall/do gas cycle |
Bit #7 | controls the A/B lattice cycle |
The best way to create a pattern for these rules is to first blank everything with the I, A, 0 sequence. Then use the screen editor to draw the gas you want in the color state 2 (plane #1 on), and draw the walls you want in the color for state 4 (plane #2) on. JC will put the texture bits back in before restarting.
l
perfumet (or perfumex) Enter
i a 0
e
F1
2
(Draw some gas)
4
(Draw some walls)
e
Enter
The program for RainZha is the exactly the same as the Faders program
listed under the Faders rule, with RN=8, L=2, U=3, K=2, and Y=2. See
the discussion of "NLUKY" rules
in the Theory chapter
for details.
There is an
NLUKY 72322
version of RainZha available in RC. The consequences of
larger choices of RN can be rapidly explored in JC by pressing L
for "load" and then entering n(RN2322),
where RN is any number between 0 and 127. You don't put spaces between the
numbers because JC already knows that the LUKY numbers will be single
digit, and that the other digits will be part of RN. For
larger values of RN it may take the rule too long to Zhabotinsky
down. You can ease up to a larger RN by repeatedly changing the
rule, increasing the RN value by, say, 10 each time. If this is
done, then the old spirals help seed the new ones.
Ranch is set to randomize planes #0 and #1 at startup. This makes Ranch work as a part of JCdemo, but has the bad effect of destroying the low two planes of any pattern you feed ranch. You might want to delete the "Rseed" lines from Ranch.PAS and rebuild Ranch.JC so that you can feed, say, Tim to it.
Ranch runs in two alternating cycles: updating the Vote boundaries and
updating the firing of Brain and Life. The cycle is controlled by bit
#7. If you randomize Ranch by pressing R, then bit #7 is
randomized as well. This puts the cells out of synch with each other,
and the rule dies wretchedly. To avoid this you can randomize the
following way: Press spacebar to halt the activity. Press R to
randomize. Press i, 7, 0 to reset all the cycle
bits. Press Enter to restart. In keystrokes:
Spacebar
r
i
7
0
Enter
The Ranch colorpalette Ranch.JCC happens to look good with many other rules, such as Hodge.
RevEcoli is based on the rule EcoLiBra,
which calculates a new
four-bit state on the basis of the present neighborhood. To make our
rule reversible, we use the high four bits of OldState to store the
prior four-bit state PastState, and we compute the NewState by the
equation:
(We add in the 16 to make sure that the mod operation never gives us a negative number.) As was explained in our discussion of the Fractal rule above, we can exchange NewState and PastState in this equation, and this guarantees reversibility. To see RevEcoli run backwards, start it up, let it run for awhile, and then swap planes #0-#3 with planes #4-#7. In keystrokes:
l
ReveEcoli Enter
Enter
(Let it run for about 30 generations)
Spacebar
F1
o 0 4 s
o 1 5 s
o 2 6 s
o 3 7 s
F1
Enter
RevEcoli very quickly turns an ordered start into seething dog barf, so it is a bit of a surprise to see the original four bit pattern re-emerge. This is even more striking if you create the original start pattern yourself, pressing e to enter the edit mode and then drawing in designs with various states 0-15. If you have a VGA and use the Default.JCC colorpalette, the picture will look more interesting. RevEcoli can turn 16-color images into secret static that can be decoded, as long as you know the process is based on EcoLiBra!
Note that the same process can be carried out for any other rule. I used EcoLiBra because it was handy, and because EcoLiBra does not use the high four bits of OldState. Since these bits were conveniently vacant, I use them to store the cell's fourbit PastState.
This suggests a fairly simple encryption scheme which can be carried out with our CA. First you and your partner need to agree on i) a number B ( 4) of bits for your rule to use, ii) a rule F which takes B bits of OldState and eight bits of neighbor state and gives a B-bit state we call F(PresentNeighborhood), iii) the number T of steps to run.
Now you and your partner create a rule RevF defined by
Knowing which F you plan to use is the "secret key" part of the transmission. There are an effectively infinite number of these keys.
Once you have RevF, you can send a message by coding your info up into a B-bit graphics screen, and running RevF on this screen for T steps. Suppose that, to make things easier for your partner, you also go ahead and use the O key to exchange the low B bits of the screen pattern with the next higher B bits of the screen pattern. Then you use Ctrl-F4 to save the pattern, as Message.JCP, and then you send Message to your partner by email or on a floppy disk. Your partner has the RevF.JC ruletable all set, and can immediately feed Message.JCP to RevF. After T cycles, your original pattern will be there on your partner's screen. Recall that the cycle number always appears on the main menu screen; although in practice it is usually pretty evident when the original message pattern has been reconstructed: all the encryption barf disappears!
In general the Rug rules will look better when the wrap is turned off. The existence of a fixed zero boundary gives the rule some information input to react to. (Recall that in the Heat rules we can fix selected cells by setting their low bits to one).
If you start a JC Rug rule on a blank screen in open nowrap mode, a
chaotic carpet will slowly grow inward, eventually filling the whole
screen. Rug averages the neighboring eight cells, but RugF averages
only the neighboring four cells. RugF runs faster, though its patterns
are prone to developing checkerboards. RugLap is a bit slower than Rug
because RugLap uses the mathematically correct averaging technique for
best approximating a solution to Laplace's equation:
Although RugLap cycles slower, it converges to a solution in many fewer steps than do Rug and RugF.
JC comes with a saved Rug pattern, the fruit of three hours computation of Rug on a blank, nowrap screen. If you press the up arrow key a few times you can break the fourway symmetry down to a bilateral symmetry. Or you can press the left arrow key a few times to mimic a touch of the RC jog-mode. Rotate your monitor 90 degrees and there's a high-res Maxine Headroom!
Many colorpalettes look good with Rug. AutoCAD gives it a sinister, seething, Giger-like quality. Stripe, Ranch, and Bob are also good colorpalettes for Rug, but best of all is Bleach, a colorpalette based on the sixteen cycle palette of RC's bleach mode.
An interesting bilaterally symmetric start for Rug can be gotten by calling eight bits of vertical texture as follows:
l
rug Enter
Alt-F6
0
8
Enter
Another good demo is:
l
ruglap Enter
c
bleach Enter
F3
F1
r
e
0(zero)
x
10 Left arrow key presses
10 Down arrow key presses
d
e
Enter
The Pascal code for the Rug rule is given in the Rule Definition chapter. RugF and RugLap are the same, except that they call, respectively, Semi4.JCO and LapInc.JCO instead of Semi8.JCO.
If you have worked with other one dimensional simulators, you will initially be confused by the fact that JC shows one dimensional rules "upside down." That is, most simulators work by updating successive lines down the screen and then scrolling new lines on from the bottom. JC does one dimensional rules differently: JC always updates the top line of the screen and then slides all the screen lines down one to make room (scrolling the screen one line is itself a cellular automaton operation). So the new lines come into a one dimensional JC simulation from the top. Thus, where conventional one dimensional simulators grow downward, JC grows upward.
Here is the code.
PROGRAM ShortPi; {A one dimensional rule that only looks at two bits of two neighbors. The rule is totalistic, meaning that it only looks at the SUM of its neighborhood. The first four digits of the totalistic lookup table are the first four digits of pi, taken MOD 4. The next six digits were found by trial and error to make a rule that looks good.} USES JCMake; {$F+} { Required for function argument to genrule. } FUNCTION JCRule(OldState,LL1,LL0,L1,L0,Self, R1,R0,RR1,RR0:integer):integer; {We will define two-bit variables LL, L, C, R, and RR covering a cell and its four nearest neighbors. This rule actually only uses L, C, and R, but we develop LL and RR so this program can be used as a template for WorldType 5 programs that do use four two-bit neighbors} VAR LL,L,C,R,RR,Sum:integer; BEGIN { Develop 2 bit values of neighbors.} LL:= 2*LL1 + LL0; L := 2*L1 + L0; C := OldState AND 3; R := 2*R1 + R0; RR:= 2*RR1 + RR0; Sum:=(L+C+R); CASE Sum OF 0: JCRule:=3; 1: JCRule:=1; 2: JCRule:=0; 3: JCRule:=1; 4: JCRule:=0; 5: JCRule:=3; 6: JCRule:=2; 7: JCRule:=0; 8: JCRule:=0; 9: JCRule:=0; END; END; BEGIN {Main} WorldType := 5; { World type: four neighbor ring } GenRule(JCRule); END.
Dewdney's SloGro is formulated in terms of a gas which moves randomly, and which is released into the system only one particle at a time. The gas we use in Soot is a many particle self-randomizing Tgas. Tgas particles move along the grid's main axes, and make ninety degree turns when they encounter another particle. The idea of having frozen cells along the boundary instead of just at the center is from a follow-up column to [DewdneyColumn88a], which is where I found the name "Soot."
The meaning of the bits in our implementation of Soot are as
follows.
Bit #0 | is the machine visible bit for update |
Bit #1 | is used for the gas |
Bit #2 | is the wall |
Bit #3 | is unused |
Bits #4 & #5 | hold a position number between 0 and 3 |
Bits #6 & #7 | control the check wall/do gas cycle If 0 do wall, if 1 do lattice A, if 2 do lattice B. |
You can put in your own gas and wall shapes by loading Soot and clearing out all the planes, putting in some random gas, using the editor to draw two disks of gas and some walls, and then turning off the editor:
l
soot Enter
F1
i a 0
i d 3
i 1 r
e
2
x (arrow keys) d (arrow keys) x (arrow keys) d
4
x (arrow keys) x (arrow keys) x (arrow keys) ... o
e
F1
Enter
This particular rule shows an interesting kind of behavior: 1D
oscillatory gliderlike patterns living on a uniform background. The
rule shows up particularly clearly if you use the colorpalette
Mask1.
PROGRAM SoundCa; {This is a JC implementation of one of the rules from the standalone SoundCa program from the RC disk. You can change this rule and key in any SoundCa rule that interests you.} USES JCMake; {$F+} FUNCTION JCRule(OldState,LL1,LL0,L1,L0,Self, R1,R0,RR1,RR0:integer):integer; {We define two-bit variables L, C, and R.} CONST RuleTable: ARRAY[0..13] OF integer=( {For an L+R Sum of: 6 5 4 3 2 1 0} {States 0 and 3 use:} 3, 2, 1, 3, 1, 2, 0, {States 1 and 2 use:} 0, 1, 0, 3, 3, 1, 0); VAR L,C,R,Sum,Index:integer; BEGIN L := 2*L1 + L0; C := OldState AND 3; R := 2*R1 + R0; Sum:=(L+R); CASE C OF 0,3: Index:=6-Sum; 1,2: Index:=13-Sum; END; JCRule:=RuleTable[Index] END; BEGIN {Main} WorldType := 5; {World type: 4 neighbor ring } GenRule(JCRule); END.
Our program starts up, by default, with the image of a cyberspace ant. Watch how John Walker's program devours the ant and scatters its remains to the wind. Turnabout's fair play! If you'd like to feed something else to this rule, take the following steps:
l
sublime Enter
F1
i 1 0
e
2
x (arrow keys) x (arrow keys) ... f
e
Enter
In order for the new gas to be interpreted in the right way, it is best for the rule cycle to be 0. If the rule has already been running for awhile, you can make sure it is in cycle 0 by stopping it with the spacebar and then pressing "i 6 0" and "i 7 0" to zero out the two cycle bits in #6 and #7.
A Pascal program
defining the Sublime rule appears in the
Rule Definition section.
This is a reversible rule like Fractal and like RevEcoli. TimeTun is a two bit rule which arises as the reversible version of a one-bit rule called Interest.
Suppose that EveryCell only looks at N, S, E, W, and Self, and suppose that EveryCell sets Interest to 0 if all these five bits are the same (zero Interest means boring), and sets Interest to 1 if any of the five neighboring plane #0 bits are different.
Now our reversible TimeTun rule is:
where is the Exclusive Or (XOR) operator, which is 1 if its two operands differ and 0 if they are the same.
TimeTun is a particularly interesting rule to watch. If you want to start it on your own pattern, simply load it, blank the screens, and use edit mode to draw something in state #1. In keypresses:
l
timetun Enter
F1
i a 0
e
1
(Draw something using X to make marks, arrow keys to move cursor, and D for disc, R for ring, B for box, O for open polygon, C for closed polygon, F for filled polygon, etc.)
e
Enter
(Let it run for awhile and then bring it back)
o s
I call it Venus because it produces a pattern that looks like what you might see peering out through hanging mosses at floating mats of vegetation in a swampy sea. When I was a child this is what science-fiction writers thought the surface of the planet Venus would be like.
Here is a listing of the Venus rule. When you start it,
it's a good idea to energize it by pressing R.
PROGRAM Venus; {Start this rule on a random pattern} USES JCmake; {$F+} FUNCTION JCRule(Oldstate,NW,N,NE,W,Self, E,SW,S,SE:integer):integer; BEGIN OldState:=OldState AND 3; CASE OldState OF 0 : JCRule:=2*(NW XOR SW) + W; 1 : JCRule:=2*(NW XOR NE) + N; 2 : JCRule:=2*(NE XOR SE) + E; 3 : JCRule:=2*(SE XOR SW) + S; END; END; BEGIN WorldType := 1; GenRule(JCRule); END.
Vote is a one-bit rule where each cell calculates the NineSum of itself and its eight neighbors, and then determines its new state on the basis of the NineSum. We can regard this as EveryCell conducting a little election between 0 and 1 among the nine cells in its neighborhood. If either 0 or 1 wins by a clear majority of 6 votes or more out of the nine votes, then that is the state which EveryCell will take on. But if either 0 or 1 wins by a scant, sneaky majority of 5 votes out of the nine, then the election is overturned, and EveryCell takes on the color of the "losing" state. Vote is discussed in more detail in the Theory chapter.
The version of Vote shown here uses bit #1 as an "echo" of bit #0. This means that cells will take on different colors if they have changed state in the last generation. You can keep rerandomizing Vote by pressing R. It's a bit startling to see what organic-looking shapes can arise from such a simple rule acting on a rectangular grid.
PROGRAM Vote; {The Vichniac voting rule on bit #0 with bit #1 used as memory} USES JCmake; {$F+} { Required for function argument to genrule. } FUNCTION JCRule(Oldstate,NW,N,NE,W,Self, E,SW,S,SE:integer):integer; VAR NineSum,NewSelf,NewState:integer; BEGIN {Function} NineSum:=NW+N+NE+E+SE+S+SW+W+Self; CASE NineSum OF 0,1,2,3,5: NewSelf:=0; 4,6,7,8,9: NewSelf:=1; END; JCRule:=(Self SHL 1) OR NewSelf END; {Function} BEGIN {Main} GenRule(JCRule) END. {Main}
If you look at VoteDNA with the Default colorpalette loaded, you will see something like Vote with thick fuzzy boundaries. To make the rule look neater, I decided to set the color for the "inland" state equal to black. But what is the inland state? It is a state X which is a fixed point for the transformation:
Replacing NewX and OldX by X and solving for X, I got X=237. The VoteDNA.JCC colorpalette was gotten by taking a colorpalette and setting the color for state 237 to black. If you want to see the color get filled back in, you can press Alt-F9 followed by 237 to be able to set the color.
The JC VoteDNA rule, started from the pattern shown above. Pattern has been shifted slightly upward.
VoteDNA makes nice thick strings out of random starts. The color patterns that move along the strings have no clear interpretation, although they do make me think of electron microscope pictures of DNA.
PROGRAM VoteDNA; {The Vichniac voting rule with the seven extra bits used as memory. The additional twist here is that we increment the memory bits by the NineSum each time.} USES JCmake; {$F+} { Required for function argument to genrule. } FUNCTION JCRule(Oldstate,NW,N,NE,W,Self, E,SW,S,SE:integer):integer; VAR NineSum,NewSelf,NewState:integer; BEGIN {Function} NineSum:=NW+N+NE+E+SE+S+SW+W+Self; CASE NineSum OF 0,1,2,3,5: NewSelf:=0; 4,6,7,8,9: NewSelf:=1; {The usual Vote rule.} END; NewState:=OldState+NineSum; {To gnash in more info.} {Now put the mem info in the 7 high bits. Do a MOD 256 by an AND $FE.} NewState:=(NewState SHL 1) AND $FE; JCRule :=NewState OR NewSelf; {Load the Vote result} END; {Function} BEGIN {Main} PalReq:='VoteDNA'; GenRule(JCRule) END. {Main}
The Tgas lives in plane #1 and the Xgas in plane #2. At any time, one
of the gasses is copied into plane #0 to be visible to the other
cells. The rule has four cycles, coded by bits #6 and #7. In cycles 0
and 1 you are updating Tgas; in cycles 2 and 3 you update Xgas. If
you want to set some gas shapes of your own, use the following
keypresses once XTC is running.
Spacebar
i a 0
e
3
(Draw something in Tgas, shown in planes #0 and #1)
4
(Draw something in Xgas, shown in plane #2)
e
Enter
Margolus and Toffoli make a interesting simile between the Zhabotinsky reaction and a reef of tubeworms. When a tubeworm feels safe, it sticks a plume out of its shell to seine the water for food. If a feeding tubeworm senses any disturbance nearby (e.g. the presence of several other feeding tubeworms), it retracts its plume and waits for a few cycles before feeding again.
In this specific rule, we suppose that each cell has four bits.
Bit #0 | is the feeding bit. |
Bits #1 and #2 | are the Time bits. |
Bit #3 | is the alarm bit. |
Margolus and Toffoli explain that variations on this rule can be
gotten by changing the conditions for the Alarm to be set to 1. The
condition "(EightSum=2)OR(EightSum>3)
" in the program listed below
makes the best patterns, but takes a really long time (two thousand
generations) to develop spirals from a random start. If you instead
use the condition "(EightSum>1)
," you get tight, squarish spirals
that converge rapidly. This fast fast Zhabotinsky rule is the rule
ZHABOFF. The merely fast Zhabotinsky rule ZHABOF uses the condition
"EightSum>2
." ZHABOF progresses not very much faster than ZHABO,
forming hardedged patterns. All three of these rules enjoy starting
out on the pattern RAT. In order to really show Zhabo
off, you can start it on a full random screen, let it run for an hour,
and then save the pattern as Zhabo.JCP. From then on,
you can start Zhabo up on Zhabo.JCP.
It might be interesting to write a rule which selects among these three conditions on the basis of the values of bits #7 and #6, and to initialize the pattern with three vertical stripes that hold the combos 10, 01, and 00 in bits #7 and #6.
PROGRAM Zhabo; {The Zhabotinsky reaction of Margolus & Toffoli} USES JCmake; {$F+} { Required for function argument to genrule. } FUNCTION JCRule(Oldstate,NW,N,NE,W,Self, E,SW,S,SE:integer):integer; VAR EightSum,Alarm,Time,NewSelf,NewState:integer; AlarmSet:Boolean; BEGIN Alarm:=(OldState SHR 3) AND 1; Time:=(OldState SHR 1) AND 3; EightSum:=NW+N+NE+E+SE+S+SW+W; IF Time=0 THEN NewSelf:=1 ELSE NewSelf:=0; IF Time>0 THEN Time:=Time-1; IF (Self=1) AND (Alarm=1) THEN Time:=3; IF (EightSum=2)OR(EightSum>3) THEN Alarm:=1 ELSE Alarm:=0; NewState:=(Alarm SHL 3) OR (Time SHL 1) OR NewSelf; JCRule:=NewState END; { Main program. } BEGIN PalReq:='Zhabo'; GenRule(JCRule); End.