FileName: tutorial_01.cyr
STATE myFirstState
ENTERING_STATE :: GOAL_AREA=200
Load or enter the code above and hit
Tab over to the
Select myFirstState and click the checkbox next to
Move your mouse over to the Petri Dish and draw your cell... it doesn't really matter what shape. A single small blob will suffice.
Check the
This basic process is how you will begin all the tutorials.
Let's turn now to the program itself.
The Internal Variable
If you put your mouse over the cell and press SPACE, some data about the cell will be displayed in the
FileName: tutorial_02.cyr
STATE myFirstState
ENTERING_STATE :: TYPICAL_AREA=200
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=1
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=1
Compile the Rules, place a myFirstState cell, and Iterate.
In your monitor, you should see that the
We accomplished this by letting
The workhorse here is
The Rules start by setting
As an analogy, this little feedback loop is like a cruise-control in your car.
When cells are squished together, or migrating, or stretched out, the Area component of their energy function may get overwhelmed by the
other energy factors. This little technique will allow your cell's
FileName: tutorial_03.cyr
STATE myFirstState
ENTERING_STATE :: TYPICAL_AREA=200; RGB(200,214,93)
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=1
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=1
Compile the Rules, place a myFirstState cell, and Iterate.
The default colors suck. Change them. Here, we've just added an
STATE myFirstState
ENTERING_STATE :: TYPICAL_AREA=50
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=1
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=1
RGB(200,214,93)
We could have also put the
FileName: tutorial_04.cyr
STATE Skin_Cell
ENTERING_STATE :: TYPICAL_AREA=50; RGB(200,34,93)
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=1
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=1
STATE Stomach_Cell
ENTERING_STATE :: TYPICAL_AREA=100; RGB(20,14,233)
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=1
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=1
STATE Eye_Cell
ENTERING_STATE :: TYPICAL_AREA=500; RGB(220,99,23)
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=2
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=2
Our Rules now have three different
FileName: tutorial_05.cyr
U_VAR apple
U_VAR bear
STATE Skin_Cell
ENTERING_STATE :: TYPICAL_AREA=50; RGB(200,34,93); apple=0; bear=255;
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=1
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=1
apple+=1; bear-=2
apple(254,INF] :: apple=0
bear[0] :: bear=255
RGB(apple, bear, 100)
STATE Eye_Cell
ENTERING_STATE :: TYPICAL_AREA=20; RGB(10,34,243); apple=20; bear=0
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=2
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=2
bear[0] :: apple+=5
!bear[0] :: apple-=5
apple(500,INF] :: bear=1
apple[20] :: bear=0
TYPICAL_AREA=apple
Here, we have declared two
Upon entering the Skin_Cell state, both variables are set to certain values.
On the fifth line in the state, we add 1 to apple and subtract 2 from bear. This will occur every cycle.
The next two lines show how variables can be used in conditions: if apple is greater than 254, it's reset to 0. Likewise, bear gets set back to 255 if it hits 0.
You can do many things with variables. Here, we use them to control the color.
In the Eye_Cell state, we use the apple variable to control the size. And, we use the bear variable as a Boolean toggle switch. That is, bear starts off at 0, and so long as it remains 0, apple will increase by 5. But, when apple hits 500, bear is set to 1 and henceforth apple's value will be reduced by 5. When apple reaches 0, bear is set back to 0 and the process repeats. All the while,
of course, the size is tracking apple causing the cell to grow and shrink. Notice the use of the 'not' signifier (the !) on the fifth sentence in the
Eye_Cell state. This condition could just as well have been written as
Keep in mind that a cell will only process the portion of the Rules pertaining to its particular
FileName: tutorial_06.cyr
U_VAR apple
STATE Skin_Cell
ENTERING_STATE :: TYPICAL_AREA=100; RGB(255,222,230); apple=0
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=1
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=1
apple+=1
apple[100] :: CHANGE_STATE{Eye_Cell}
STATE Eye_Cell
ENTERING_STATE :: TYPICAL_AREA=40; RGB(140,240,233); apple=0
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=2
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=2
apple+=1
apple[75] :: CHANGE_STATE{Skin_Cell}
Here, we use apple as a timer. It starts at 0 in the Skin_Cell state. When it reaches 100, the
Not very exciting. But, place a dozen or so of each type of cell in the Petri Dish... you'll witness how the
This would also be good time to
Finally, if you place a bunch of cells, you'll notice that they all switch back and forth in a regular manner... very unlife-like. We'll fix that in the following section with the
FileName: tutorial_07.cyr
U_VAR apple
STATE Skin_Cell
ENTERING_STATE :: TYPICAL_AREA=100; RGB(255,222,230); apple=0
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=1
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=1
POISSON[10] :: apple+=10
apple[100,INF] :: CHANGE_STATE{Eye_Cell}
STATE Eye_Cell
ENTERING_STATE :: TYPICAL_AREA=40; RGB(140,240,233); apple=0
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=2
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=2
POISSON[4] :: apple+=4
apple[75,INF] :: CHANGE_STATE{Skin_Cell}
There are just a few subtle changes in Skin_Cell. Instead of incrementing apple each time, we're
only going to increment it if
(Since apple is only being incremented every 10 times on average, we've made it increment by 10 just so that we'll get, more or less, the same pace as the last example.)
Similar changes have been made in Eye_Cell.
Now, when you place a few dozen cells of each type, they will switch back and forth in Poisson-ishy random manner.
You will note that the conditionals in the last line of each
FileName: tutorial_08.cyr
G_VAR ooze
G_VAR mucous
U_VAR apple
U_VAR cat
STATE Skin_Cell
ENTERING_STATE :: TYPICAL_AREA=100; RGB(255,222,230); apple=0; cat=0;
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=2
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=2
POISSON[2] :: apple+=1;
cat=0
apple[0,10] :: cat=1;
cat[1] :: TYPICAL_AREA+=30; EMIT{ooze}
cat[0] :: TYPICAL_AREA=100
apple[100,INF] :: apple=0;
STATE Eye_Cell
ENTERING_STATE :: TYPICAL_AREA=50; RGB(4,222,30); apple=0;cat=0;
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=2
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=2
POISSON[2] :: apple+=1;
cat=0
apple[0,10] :: cat=1;
cat[1] :: TYPICAL_AREA+=30; EMIT{mucous}
cat[0] :: TYPICAL_AREA=50
apple[100,INF] :: apple=0;
STATE Probe_Cell
ENTERING_STATE :: TYPICAL_AREA=50; RGB(70,70,250)
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=2
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=2
RGB(70,70,250)
mucous[0.01,INF]; mucous(ooze,INF] :: RGB (70,250,70)
ooze[0.01,INF]; ooze(mucous,INF] :: RGB (250,70,70)
We've defined two
Incorporating some of the lessons learned so far, you'll notice that apple and cat are being used to make the Skin_Cell and Eye_Cell swell every so often. Why do we make it swell?
No reason... just looks cool. But, you'll notice that when the cell does swell, it also processes the command
The Skin_Cell will emit ooze, and the Eye_cell will emit mucous.
When the
Each
Note that the values diminish very quickly. If you are going to have a cell change its behavior based on a
(We've been asked why the
FileName: tutorial_09.cyr
G_VAR ooze
V_VAR myFirstVector
STATE Sun_Cell
ENTERING_STATE :: TYPICAL_AREA=600; RGB(245,245,66)
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=2
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=2
EMIT{ooze}
STATE Uranus_Cell
ENTERING_STATE :: TYPICAL_AREA=150; RGB(131,232,245)
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=2
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=2
SET_VEC{myFirstVector}[ooze]; MIGRATE[myFirstVector,-60,90]
STATE Venus_Cell
ENTERING_STATE :: TYPICAL_AREA=150; RGB(31,232,145)
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=2
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=2
SET_VEC{myFirstVector}[ooze]; MIGRATE[myFirstVector,-50,-90]
STATE Asteroid_Cell
ENTERING_STATE :: TYPICAL_AREA=80; RGB(186,156,147)
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=2
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=2
SET_VEC{myFirstVector}[ooze]; MIGRATE[myFirstVector,-60]
STATE Comet_Cell
ENTERING_STATE :: TYPICAL_AREA=80; RGB(255,255,223)
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=2
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=2
SET_VEC{myFirstVector}[.5,.5]; MIGRATE[myFirstVector,-40]
Cells move. Their movement is a vital component of morphogenesis.
In order to tell the cell which way to move, we need to declare a
We set the direction of a
Here, we'll explore the second and third possibilities.
Let's start with the last cell first.
The Comet_Cell sets the
Now, Uranus_Cell, Venus_Cell, and Asteroid_Cell have their
The
The
PUTTING IT ALL TOGETHER: Place a Sun_Cell in the middle of the Petri Dish. Sprinkle a few Uranus_Cells and Venus_Cells around it. When the ooze diffuses out to your 'planets,' they will start orbiting. Yes, CYCELL can double as an orbital mechanics simulator. (Not really.)
Check
FileName: tutorial_10.cyr
The
Another thing to notice is the use of
With that out of the way, let's start with unchecked_growth. This cell type
will undergo mitosis continuously, so long as
Now, unchecked_growth will completely fill up the Petri Dish.
How does one stop the cells from splitting?
This is actually a very good question. What makes your nose stop growing? Nobody really knows. In fact, there is a fairly good chance you will die someday precisely because a few cells in your body mutate and lose their ability to know when to stop growing. (i.e. cancer.)
There are many techniques you can come up with in CYCELL to limit cell-growth. We've implemented one, as an example, in the next state.
You'll notice that controlled_growth sets the
With bud_1 we begin taking advantage of
You'll notice that the cleavage plane of the cell is random.
With bud_2 we take control of how our cell divides. Directional mitosis is a thing in real biological systems.
As with
In the example,
We use the
We first place a cell from the uncontrolled_growth state. Then, we place a couple of controlled_growth cells. Then, we place a bud_1 and a couple bud_2s.
The cell types bud_1 and bud_2 will grow forever. The quick technique we used for controlled_growth isn't all that useful here. If we wanted our bud_2 to make a long snaking tendril with 100 or so cells and then stop, our growth_factor variable would need to start out at something like 2^100. That's silly.
You will be tempted to think that there must be some kind of counter that we could implement that will tell our bud_2 cell to quit dividing after, say, 100 splits. But, it doesn't work that way, and that's precisely what we were getting out with the fourth point in the "Brief Overview" section above. Specifically, although it might look like its the same bud_2 cell moving along and spitting out stalk cells, in reality after mitosis we're left with two brand-new cells, so to speak, who have never undergone mitosis before! Not to get all philosophical about it, but in truth, a cell can't undergo mitosis more than once, for after mitosis it's no longer the same cell.
What we really want is a way to count the generations of a cell. That's a tricky thing to keep track of. For, after mitosis, every variable into which we could encode such a counter is chopped in two, making it very unwieldy to maintain some permanent record of a cell's biography. (Not impossible... just unwieldy.)
FileName: tutorial_11.cyr
There's a lot of stuff going on with this one. We'll walk you through it.
Let's start with the end-result and work backwards. We're going to grow a scrum of Skin cells. Some of them will
be Macrophages. These blueish Macrophages comprise our scrum's immune system.
Occasionally, one of the Skin cells will turn into a greenish-Mutant cell! Luckily, the Mutant cell exudes
a protein, which is spread from cell to cell (with a reddish-tint.) When our Macrophages sense it, they
Now, on to the code.
First, the
The real point of this lesson pertains to
Unlike
When you run these Rules, you will see the Mutant cells transport their intruder_alert into the adjoining cells. And, those cells do the same... We've linked the Skin cell's color to the level of intruder_alert so you will be able to see the spreading intruder_alert as a red tint.
The Stem cells create a modest-size scrum just to get things started. You'll notice that about every 5% of their mitoses will create a Macrophage cell. We've accomplished this using the
Once we have the appropriate number of cells in the Petri Dish (controlled, once again, by the multiple halving of a generic
On the 10th line under the Skin cell definition, we force the cell to turn into a Mutant. There are three conditions.
The first pertains merely to
aesthetics; we don't want mutants to arise on the periphery, so we require that none of the Skin cells can have any membranes touching the
The second condition requires that there not be any intruder_alert in our cell, and the third is just a
The Mutant cells, when they arise, maintain a constant level of intruder_alert at 300. At each cycle, due to the
The 4th through 8th lines under the Skin cell definition are worth looking at. They deal with how the cell is colored. We want a reddish-tint to correspond to the
level of intruder_alert. With computer graphics, to make something more red, we decrease the levels of green and blue. We've used a little
throw-away
Meanwhile, the Macrophage cells are just hanging out. But, when their level of intruder_alert exceeds 0, they spring into action. Using
When a Macrophage makes contact with a Mutant, a counter begins. You'll notice we make the count faster depending on how many Macrophage cells are touching the Mutant. When the count reaches 100, the Mutant turns back into a normal Skin cell.
FileName: tutorial_12.cyr
Hehehe... This one's a monster. It incorporates nearly everything discussed so far.
It's included to demonstrate how difficult it is to use mere rules to create a complex form. This lizard-ish looking form might self-assemble most of the time. But, there are a gazillion ways it can go wrong.
While we won't go through this one step-by-step, a few standard techniques are worth mentioning.
First, as the Bud grows a tendril, the cells it produces emit a
Second, while the thing is grown, a
Once we have the sections defined, we can place leg-buds at the intersections of the three components. These then
grow until they are outside the range of the left-over
Play around with it... there are a million different ways to make it go ka-ployie.
It's an ad-hoc, unreliable, and brittle system. There must be something more behind self-assembly. The next set of tutorials reveal what that something might be.