The World Vol 4 · The Terrarium
ch 39 / 105
Chapter 39

A Species Is a Row of Numbers

The species block

Every plant in the terrarium has used the same physiology so far: the same light share, thirst, hunger, upkeep and root reach. The drawing side has had the same problem, with grammar settings typed on the command line instead of belonging to a kind of plant. A species is one row of parameters read by the budget and the turtle; changing the row changes the plant, while the ledger, bed, light grid and drawing code stay the same.

Four rows make four claims about the same valley. Moss spends little and spreads low. Herb races through one season. Scrub survives dry ground. Tree buys height and pays for it in slow growth.

The row is laid out now the way a genome can hold it later, but it is not a genome here. No mutation, crossing or selection exists on this page. The point is smaller: the code reads numbers from one block instead of from scattered constants.

The fourteen-number block

The honest way to write the block is to go through the code and collect every number something already reads. On the drawing side there are four: which rule set in the grammar library, the branch turn written as one part in so many of a whole turn, how many world pixels one stroke covers, and a three-number price list saying what one stroke weighs, how many strokes a generation multiplies by, and the generation the grammar is never grown past.

On the living side there are nine. The grams a seed germinates at. The share of its cell's daylight the crown intercepts, the water spent per unit of energy fixed, the nutrient spent per unit fixed, and the upkeep charged on every gram already standing. How far the roots reach. How far the crown reaches, how much leaf a gram carries, and how much height a gram stands. That is the whole list, and the list is closed: a number that no method reads is a number that cannot make one plant differ from another.

▣ Build · stage 1 — the block, and the two ways a row is spent
// internal/terra/species.go

// Species is one kind of plant written down as numbers. The first half
// is the form: which grammar draws it, how far a branch swings, how
// long one stroke is, and what each generation of that grammar costs in
// grams. The second half is the physiology: what a gram earns, what it
// drinks and eats to earn it, what it costs to keep, and how far the
// roots and the crown reach. There is nothing else. Two plants of
// different kinds run the same code and differ only here.
type Species struct {
	Name string

	// the form
	Grammar string  // the rule set in grow.Named this plant grows by
	Part    float64 // a branch turns one part in this many of a turn
	Seg     float64 // world pixels one drawn stroke covers
	Form    Form    // grams generation n of that grammar needs standing

	// the physiology
	Seed   float64 // grams it germinates at
	Catch  float64 // the share of its cell's light the crown intercepts
	Thirst float64 // water units spent per energy unit fixed
	Hunger float64 // nutrient units spent per energy unit fixed
	Upkeep float64 // energy one gram of standing tissue costs a tick
	Root   float64 // how far the roots reach, in cells
	Reach  float64 // how far the crown reaches, in cells
	Leafy  float64 // cells of leaf area carried per gram
	Tall   float64 // cells of height per gram
}

// Physiology is the budget half of the row, handed to the ledger the
// first three chapters of this volume wrote.
func (s Species) Physiology() Plant {
	return Plant{Mass: s.Seed, Catch: s.Catch, Thirst: s.Thirst,
		Hunger: s.Hunger, Upkeep: s.Upkeep, Root: s.Root}
}

// Canopy is the crown half of the row, handed to the light field.
func (s Species) Canopy() Canopy {
	return Canopy{Crown: s.Reach, Leafy: s.Leafy, Tall: s.Tall}
}

// Germinate turns a row of numbers into one plant standing somewhere.
// Every trait is copied into the individual and the row is never
// consulted for a rate again, so nothing a plant does can reach back
// and edit the kind it came from.
func (s Species) Germinate(at sim.Coord, t int) *Stand {
	kind := s
	return &Stand{Name: s.Name, At: at, Kind: &kind,
		Plant: s.Physiology(), Crown: s.Canopy(), Born: t, Sown: -1}
}

// Draft is the form half of the row, handed to the grower chapter 33
// wrote, grown to whatever generation this much tissue can pay for. The
// picture and the budget meet in exactly one place: the generation
// number, which is a price.
func (s Species) Draft(mass float64, seed uint64) grow.Plant {
	g := grow.Named[s.Grammar]
	return grow.Plant{Axiom: g.Axiom, Rules: g.Rules, Gen: s.Form.Stage(mass),
		Seg: s.Seg, Turn: field.Turn / s.Part, Up: field.Turn / 4, Seed: seed}
}

Four methods, and not one of them invents a number: every value the four hand out came off the row. Physiology and Canopy hand slices of it to machinery that already existed and was already taking those exact fields, so neither of them is a conversion so much as a re-labelling. Germinate copies twice. The budget and the crown are stamped into the individual as plain values, and the row itself is copied into a local variable the new stand then points at, so what a plant carries is its own private duplicate of the table line it came from. Two plants of one species are two independent sets of numbers that started out agreeing, and neither of them can reach the line in Flora they were stamped from.

What Germinate hands back is wider than the stand the shading chapter published. That chapter had no species for a plant to point at, so a plant there was a position, a budget, a crown, the water under it and the light over it, and nothing on it could say what kind of thing was standing there. A filled-in row makes that sayable, and it also gives an individual a few facts a kind cannot have. Here is the type as this page leaves it.

// internal/terra/sky.go

// Stand is one plant standing somewhere: the budget of the physiology
// chapters, the crown it carries, the row it was stamped from, and the
// light that reached the top of that crown this tick.
type Stand struct {
	Name  string
	At    sim.Coord
	Kind  *Species
	Plant Plant
	Crown Canopy
	Water float64 // water units the ground under it can give up in a tick
	Lit   float64 // light reaching the top of its crown, set by Cast
	Born  int     // the tick it germinated on
	Sown  int     // the year it last spent mass on seed, or -1
	Last  Ledger
}

The five new fields are all of one sort: what this plant knows that its row does not. Name is the row's name copied down, so a stand can be printed or counted on its own, without whoever is counting holding the table open beside it. Kind is the pointer to the private duplicate, which lets a plant be asked what it is and still leaves it no way to edit the line in Flora it came from: the copy belongs to the plant, the table belongs to the valley. Born is the tick it germinated on, which is a fact no row could ever hold, because a row is a kind and a kind has no birthday. Sown is the year it last spent mass on seed. It reads −1 for never, and it reads −1 on every plant on every tick of this page, since a valley whose sky never changes has no years in it and nothing here has anywhere to put a seed.

Last is the ledger line this plant spent on the previous tick, kept on the plant instead of being handed out by whatever is running the loop. Every tick loop below writes st.Last = l as it goes, and that one line is where the limit column comes from in the tables that print where a run finished up: two thousand ticks go by with nothing printed, and then the plant is asked what stopped it last. The comment over the type moved with the fields. It used to name the ground a plant is rooted in as the third thing a stand holds, and the row it was stamped from is the better answer now that there is a row to point at.

Draft is where the two halves of the block finally touch, and they touch at one number. The grammar knows nothing about energy. The ledger knows nothing about strokes. Between them sits a price list, and the only thing the budget hands the drawing is the mass it managed to stand up; the drawing hands back the largest generation that mass can pay for. Change any physiology number and the picture changes, without a single line of drawing code hearing about it.

One row of numbers and the code that reads each one On the left, the fourteen numbers of a species row in two groups. The form group holds the grammar name, the branch part and the stroke length, and below them the price list of generations 0 to 3 in grams. The physiology group holds the seed mass, then catch, thirst and hunger, then upkeep, then the root radius, then the crown radius, leaf per gram and height per gram. On the right, five boxes of machinery: the grower and turtle, the generation picker, the ledger, the bed, and the canopy. Gold arrows run from each group of numbers to the one box that reads it: the grammar name, part and stroke go to the grower; the price list goes to the generation picker; seed, catch, thirst, hunger and upkeep go to the ledger; the root radius goes to the bed; and the crown numbers go to the canopy. Along the bottom, a highlighted strip holds the mass, the one number a tick changes, written by the ledger and read by the price list. ONE ROW OF NUMBERS, AND THE CODE THAT READS EACH ONE THE FORM grammar part seg gen 0 .. gen 3, in grams THE PHYSIOLOGY seed catch thirst hunger upkeep root crown leafy tall grow.Grow, then the turtle Form.Stage picks a generation Plant.Limit, then Plant.Tick Bed.Roots and Bed.Draw Canopy.Pass and Canopy.Height mass the one number a tick changes the ledger writes it, the price list reads it
Figure 39.1 — nothing on the right knows what species it is holding. Every arrow ends at code that was written before any species existed, and the two halves of the row never speak to each other except through the highlighted strip at the bottom.

Four species rows

Now fill four of them in. A strategy is a bet, so say what each row is betting before reading a single number off it.

The moss bets on leftovers. It is cheap to keep and it catches almost nothing, so it can never be big, and the price list of its grammar is priced in tenths of a gram so it does not need to be. The herb bets on open ground and on getting there first: it catches more of its cell's daylight than anything else in the table and pays for that with expensive tissue, which caps it low and gets it there fast. The scrub bets on ground the others cannot pay for. It spends a quarter of a water unit where the herb spends nine tenths, and it spreads its roots over three cells' radius so that no single cell is asked for much. The tree bets on time. Its tissue is the cheapest in the valley to keep, which makes it the slowest thing here and the largest, and everything it earns goes into height nothing else can reach.

▣ Build · stage 2 — four rows, and what adding a fifth would cost
// internal/terra/species.go

// Flora is the valley's species list: four rows, four strategies, one
// set of machinery. Adding a fifth kind of plant to this world is
// adding a line to this table.
var Flora = []Species{
	{
		Name: "moss", Grammar: "fan", Part: 5, Seg: 1,
		Form:  Form{SegMass: 0.10, Fan: 4, Max: 2},
		Seed:  0.25,
		Catch: 0.06, Thirst: 0.30, Hunger: 0.04, Upkeep: 0.020, Root: 1.0,
		Reach: 0.5, Leafy: 0.020, Tall: 0.002,
	},
	{
		Name: "herb", Grammar: "sprig", Part: 8, Seg: 2,
		Form:  Form{SegMass: 2, Fan: 5, Max: 3},
		Seed:  1.00,
		Catch: 0.30, Thirst: 0.90, Hunger: 0.20, Upkeep: 0.050, Root: 1.5,
		Reach: 1.5, Leafy: 0.050, Tall: 0.022,
	},
	{
		Name: "scrub", Grammar: "fan", Part: 12, Seg: 2.5,
		Form:  Form{SegMass: 2.5, Fan: 4, Max: 3},
		Seed:  0.50,
		Catch: 0.12, Thirst: 0.25, Hunger: 0.06, Upkeep: 0.008, Root: 3.0,
		Reach: 2.5, Leafy: 0.025, Tall: 0.012,
	},
	{
		Name: "tree", Grammar: "sprig", Part: 14, Seg: 3,
		Form:  Form{SegMass: 4, Fan: 5, Max: 4},
		Seed:  2.00,
		Catch: 0.25, Thirst: 0.60, Hunger: 0.15, Upkeep: 0.005, Root: 2.5,
		Reach: 2.5, Leafy: 0.030, Tall: 0.030,
	},
}
$ go run ./cmd/flora -mode row
flora: 4 rows, and what each one predicts before a tick is taken
       12.00 of daylight on an open cell, a plant at 4,1

          grammar   part    seg    gen 0    gen 1    gen 2    gen 3
  moss        fan      5    1.0     0.10     0.40     1.60     6.40
  herb      sprig      8    2.0     2.00    10.00    50.00   250.00
  scrub       fan     12    2.5     2.50    10.00    40.00   160.00
  tree      sprig     14    3.0     4.00    20.00   100.00   500.00

           seed  catch  thirst  hunger  upkeep   root  crown   tall
  moss     0.25   0.06    0.30    0.04   0.020    1.0    0.5  0.002
  herb     1.00   0.30    0.90    0.20   0.050    1.5    1.5  0.022
  scrub    0.50   0.12    0.25    0.06   0.008    3.0    2.5  0.012
  tree     2.00   0.25    0.60    0.15   0.005    2.5    2.5  0.030

          soil   income   ceiling    gen    tall    pass alive at branches at
  moss       4   0.7200     36.00      2    0.07  0.2800   0.0833      0.1333
  herb       6   3.6000     72.00      2    1.58  0.6000   0.1667      1.6667
  scrub     12   1.4400    180.00      3    2.16  0.7857   0.0333      0.6667
  tree      10   3.0000    600.00      3   18.00  0.1429   0.0400      0.4000

Two of the four grow by the same rule set and nobody would put them in the same species. The moss and the scrub both grow by fan, one at a fifth of a turn and one stroke to the pixel, the other at a twelfth of a turn and two and a half pixels a stroke, and the price of a generation differs between them by a factor of twenty-five. Swapping a rule set is the biggest lever in the block, and it is not the only one that changes what a plant looks like.

The third table is where the bets turn into arithmetic, and not one figure in it came from a run. The soil column is how many cells of real ground the root radius finds from that cell, so the scrub's radius of 3 reaches twelve where the moss's radius of 1 reaches four. The income is the first half of the budget chapter's arithmetic: the light landing multiplied by the crown's share, or as much of that as the ground can pay for. The ceiling is that income divided by upkeep. Read the ceiling column against the price list above it: the tree clears the 500 grams generation 3 costs with a hundred to spare, and the generation after that wants five times 500, which nothing in this valley is ever going to earn.

Two columns on the right are new, and they are the numbers that decide whether a row can live somewhere.

∑ Interlude — the two lights a row has to clear

Numbers first. A moss seed germinates holding 0.25 grams. Its upkeep on that is 0.020 × 0.25 = 0.005 energy units a tick, and it has to earn at least that much or it starts burning tissue on its first tick. It earns 0.06 of whatever light reaches it, so the light it needs is 0.005 ÷ 0.06 = 0.0833 units. Below that figure a moss seed is smaller on tick 2 than it was on tick 1, and it never recovers.

Write the upkeep per gram u, the seed's mass in grams s, and the crown's share of the light c. The light a seed needs to hold its own weight is the same three numbers in the same order:

alive = u · s ÷ c

0.020 × 0.25 ÷ 0.06 = 0.0833, which is the moss's entry in the alive at column, and 0.005 × 2.00 ÷ 0.25 = 0.0400 is the tree's. Being alive is a low bar. The second figure asks something harder: what light does a plant need before it can afford the first branching of its own grammar. That is the same division with the price of generation 1 standing in for the seed. Call it g:

branches = u · g ÷ c

For the moss, 0.020 × 0.40 ÷ 0.06 = 0.1333. For the herb, 0.050 × 10.00 ÷ 0.30 = 1.6667, more than twelve times as much light for the same achievement. The two figures together say something the ceiling alone cannot. A plant can clear the first bar and fail the second for its whole life, alive and never once anything but a single stroke of stalk, and the gap between a row's two bars is decided as much by the price of its grammar as by anything in its physiology.

uupkeep: energy one gram of standing tissue costs per tick
sthe grams a seed of this kind germinates holding
cthe share of the light reaching it that its crown intercepts
ggrams generation 1 of this row's grammar costs to stand up
alivethe least light at which a seed does not shrink on its first tick
branchesthe least light at which it can afford to be more than one stroke
▣ Build · stage 3 — one row, running, with the picture keeping up
$ go run ./cmd/flora -mode alone -only scrub -ticks 600 -every 100
flora: one plant of each kind, each alone in its own 12x8 valley at 4,1
       12.00 of daylight, the ground putting back 0.60 of moisture and 0.150 of nutrient a cell a tick

  scrub, 600 ticks
    tick       mass   caught    grown  ceiling    gen  segments      limit
       1     1.9360   1.4400   1.4360   180.00      0         1      light
       2     3.3605   1.4400   1.4245   180.00      0         1      light
       3     4.7736   1.4400   1.4131   180.00      0         1      light
     100    99.6045   1.4400   0.6484   180.00      2        16      light
     200   143.9920   1.4400   0.2904   180.00      2        16      light
     300   163.8725   1.4400   0.1301   180.00      3        64      light
     400   172.7767   1.4400   0.0583   180.00      3        64      light
     500   176.7648   1.4400   0.0261   180.00      3        64      light
     600   178.5510   1.4400   0.0117   180.00      3        64      light

The two right-hand columns are the block's two halves keeping step. The scrub is a single stroke until it has 10 grams standing, four strokes after that, sixteen once it clears 40 grams, and sixty-four when it passes 160 somewhere between tick 200 and tick 300. The ceiling column never moves, because the ground under this plant is being refilled faster than one scrub can drink it, so the only thing holding it back is the light, exactly as the paper table said. Nobody typed a generation anywhere in this run.

The trickle is stated on the second line because it is a decision, not a fact of the world. This page holds the ground generous on purpose so that four rows can be compared without the soil deciding the argument, the way the shading chapter held the water flat so that only height varied. The next stage turns it down and the argument changes completely.

▣ Build · stage 4 — four rows, four valleys, the same two thousand ticks
$ go run ./cmd/flora -mode alone -ticks 2000
flora: one plant of each kind, each alone in its own 12x8 valley at 4,1
       12.00 of daylight, the ground putting back 0.60 of moisture and 0.150 of nutrient a cell a tick

               mass   height     leaf    gen  segments      limit            world
  moss      36.0000   0.0720   0.7200      2        16      light 05f1078fa6186607
  herb      72.0000   1.5840   3.6000      2        25      light 9f20a8ae37b2020c
  scrub    180.0000   2.1600   4.5000      3        64      light 7c7adbc0c7bf6090
  tree     599.9735  17.9992  17.9992      3       125      light 0504e58f74b3fb24

36.0000, 72.0000, 180.0000 and 599.9735, against a paper table that predicted 36, 72, 180 and 600 before anything ran. Only the tree misses, by 0.0265 of a gram, and it misses for the reason the budget chapter gave: a plant closes the same fraction of its remaining distance every tick and never quite arrives. That fraction is the upkeep, and the tree's is the smallest in the table at five thousandths, so it is the one row that two thousand ticks is not long enough to finish.

The turtle from the row

The drawing side has been able to put a plant on a frame since the grammar chapter, and it has never been told what a species is. It does not have to be. A row supplies the rule set, the turn, the stroke length and, through the price list, the generation; the turtle takes those and hands back segments; the line routine paints them. All of it is one function with no switch in it, and the bench calls that one function four times.

▣ Build · stage 5 — one drawing routine, run four times
// cmd/flora/shot.go

// sprout grows one row of the species table at one mass and paints it
// into a buffer at one column. Nothing in here asks which species it is
// holding: the row is handed to the grower chapter 33 wrote, the turtle
// reads the string it produces, and the same three lines draw a moss
// and a canopy tree.
func sprout(b *render.Buffer, s terra.Species, mass float64,
	x, base int, pseed uint64) (int, int, grow.Box) {

	d := s.Draft(mass, pseed)
	str, segs := d.Sprout(vec.Vec2{X: float64(x), Y: float64(base)})
	if _, ok := grow.Balanced(str); !ok {
		die(fmt.Errorf("%s at %.2f grams: the brackets do not close", s.Name, mass))
	}
	for _, sg := range segs {
		draw(b, sg)
	}
	return d.Gen, len(segs), grow.Bounds(segs)
}
$ go run ./cmd/flora -mode shot -shot assets/frames/flora-four-rows.png
flora: 4 rows on the bare frame, out of row 110
          grammar   part    seg      grams    gen   segments     w by h  its own frame
  moss        fan      5    1.0      36.00      2         16     4 by  4  115643045c8a8fb1
  herb      sprig      8    2.0      72.00      2         25     9 by 18  95ee7b9effabe33a
  scrub       fan     12    2.5     180.00      3         64    14 by 20  66de1958cdd84ce4
  tree      sprig     14    3.0     600.00      3        125    27 by 81  877e7b705837ea3a
  the four together 843f8622babfd8d836e08dde6f2e0f60288573d0a60c479779028a15bdafedc7
  wrote assets/frames/flora-four-rows.png

Each row is drawn twice: once alone on an empty frame, which is where its own digest comes from, and once into the shared frame with the other three. Separating them is cheap and it buys a real thing. If a later edit changes what a moss looks like, exactly one of those five digests moves. If it changes the line routine or the colour ramp, all five move at once. A single hash over the finished picture cannot tell those two apart, and they call for opposite reactions.

The size column is the physiology showing through the drawing. Four pixels by four for the moss and twenty-seven by eighty-one for the tree, from one turtle, because 36 grams buys 16 strokes of one pixel and 600 grams buys 125 strokes of three.

Four green plants on a near-black background, left to right. The first is a tiny four-pixel smudge. The second is a small upright sprig with a few short side branches. The third is wider and lower, a squat fan of pale twigs. The fourth is much larger than the rest, a branched stalk four times the height of its neighbours with dark lower limbs and pale outer twigs.
Figure 39.2assets/frames/flora-four-rows.png: the moss, the herb, the scrub and the tree, each at the mass its own row settles at, drawn by the same turtle in the same three greens. The size difference is 0.020 of upkeep against 0.005, and not a drawing decision.

The tradeoff table

Four rows that each do well on good ground in full sun say nothing. A strategy is a claim about bad conditions, so put them in some.

▣ Build · stage 6 — the same four seeds, under one, two and three grown trees
$ go run ./cmd/flora -mode under
flora: a seedling under grown trees, each standing at 600.00 grams
       one crown spreads 18.00 cells of leaf over 21 cells, letting 0.1429 of the light through

   crowns     light                 moss                 herb                scrub                 tree
                      ceiling  gen  segs   ceiling  gen  segs   ceiling  gen  segs   ceiling  gen  segs
        0   12.0000    36.000    2    16    72.000    2    25   180.000    3    64   600.000    3   125
        1    1.7143     5.143    2    16    10.286    1     5    25.714    1     4    85.714    1     5
        2    0.2449     0.735    1     4     1.469    0     1     3.673    0     1    12.245    0     1
        3    0.0350     0.105    0     1     0.210    0     1     0.525    0     1     1.749    0     1

One crown overhead costs every row about six sevenths of its ceiling, and the moss is the only one that loses nothing at all from its picture: still generation 2, still sixteen strokes, at a seventh of the mass. Its grammar was priced for a plant that was never going to be big. Under two crowns the moss is the only one of the four that is still a plant at all; the other three are single strokes with a ceiling above their seed mass, alive and drawing one line. Under three crowns the answer changes hands. Compare each ceiling in that last row against the seed masses in the table above: 0.105 against the moss's 0.25, 0.210 against the herb's 1.00, 1.749 against the tree's 2.00, and 0.525 against the scrub's 0.50. Three of the four are already burning tissue on their first tick. The scrub clears it by twenty-five thousandths of a gram, which is the alive at column being right: 0.0333 units of light, the lowest bar in the valley, and there is 0.0350 available.

▣ Build · stage 7 — the same four rows on ground that has almost nothing to give
$ go run ./cmd/flora -mode alone -ticks 2000 -rain 0.05 -feed 0.01
flora: one plant of each kind, each alone in its own 12x8 valley at 4,1
       12.00 of daylight, the ground putting back 0.05 of moisture and 0.010 of nutrient a cell a tick

               mass   height     leaf    gen  segments      limit            world
  moss      33.3333   0.0667   0.6667      2        16      water 18c2c548bb305741
  herb       6.0000   0.1320   0.3000      0         1   nutrient dbe4382a74f3ddb2
  scrub    180.0000   2.1600   4.5000      3        64      light 7c7adbc0c7bf6090
  tree     133.3296   3.9999   3.9999      2        25   nutrient bdbd9464f3115df6

The trickle is down by a factor of twelve and fifteen and the scrub has not noticed. Its digest is the same 7c7adbc0c7bf6090 as the generous run, to the last bit of the last cell, because twelve cells refilling at 0.05 still pay for more energy than its crown can catch, and a plant that is light-limited does not care what the ground has spare. That is the drought bet paid in full, and it was bought with a crown that intercepts 0.12 where the herb's takes 0.30.

The herb is the same bet from the losing side. It falls from 72 grams to 6, and 6 is below the 10 grams its own first branching costs, so the fastest plant in the valley spends two thousand ticks as one stroke of stalk. Its limit reads nutrient, which is the interesting part: it is not the water that finished it despite being the thirstiest row in the table, but the nutrient, because catching three tenths of the daylight means eating 0.20 of nutrient for every unit of that daylight it fixes, out of six cells. The tree comes down to 133.3296 grams, less than a quarter of what it manages on good ground, by the same nutrient arithmetic on ten cells, and it is still the second largest thing here.

▣ Build · stage 8 — all four in one valley, sharing one sky and one bed
$ go run ./cmd/flora -mode mixed -ticks 2000 -every 400
flora: one of each kind in one 12x8 valley, 2000 ticks
       moss at 3,1 herb at 4,1 scrub at 6,1 tree at 5,1

    tick       moss       herb      scrub       tree  moss lit  herb lit scrub lit  tree lit
       1      0.959      4.540      1.932      4.990    11.899    11.966    11.966    12.000
     400      9.398     17.949     63.416    519.474     2.792     2.867     3.102    12.000
     800      5.512     11.070     32.445    589.157     1.784     1.828     1.901    12.000
    1200      4.947     10.119     26.687    598.540     1.642     1.684     1.739    12.000
    1600      4.870      9.991     25.848    599.803     1.622     1.665     1.718    12.000
    2000      4.860      9.974     25.732    599.974     1.620     1.662     1.715    12.000

  moss       4.860 grams where alone it reaches    36.000,  0.010 cells tall, generation 2 in 16 strokes
  herb       9.974 grams where alone it reaches    72.000,  0.219 cells tall, generation 0 in a single stroke
  scrub     25.732 grams where alone it reaches   180.000,  0.309 cells tall, generation 1 in 4 strokes
  tree     599.974 grams where alone it reaches   599.974, 17.999 cells tall, generation 3 in 125 strokes
  world a56a7bce69588ab0

Read the tick 400 row and then the tick 2000 row. The other three all peak somewhere in the first four hundred ticks and then come back down, because the tree is still growing underneath them and the light over their heads keeps falling: 2.792 units at tick 400, 1.620 at tick 2000. Nothing kills them. They are carried down to whatever their own ceiling is under the light they end up with, and a ceiling a plant has already passed makes it shrink. The tree's own column reads 12.0000 on every printed row and it finishes on 599.974 grams, which is what it weighs standing alone in an empty valley, to three decimal places.

The moss is the one to look at hardest. It ends at 4.860 grams, a seventh of what it manages alone, and it is still generation 2 in sixteen strokes: the only one of the four that is the same plant in the shade as it is in the open. The herb, which was the fastest grower in the valley by a wide margin at tick 400, ends as a single stroke.

One warning before the failure below. The rows above were designed by moving numbers around until the four bets came out distinct, and nothing in the code checks a row for sense. A row with the tree's upkeep, the herb's catch and the scrub's thirst is legal, and the world would grow it happily into something better than everything else at everything. The trade-offs live in the author's head and in the comments, and not in a single line of Go.

⚠ Worked failure — every knob that sounded like shade tolerance

The mixed run leaves an obvious job. The tree wins the light and its own seedlings would germinate into the shade underneath it, so the table needs a row that can establish itself down there: a sapling with a bigger seed to start on, a wider crown to gather what little light gets through, and more leaf on every gram to gather it with. Three edits, all of them things anybody would say about real trees without stopping to check, and every one of them is already a field in the block.

// cmd/flora/main.go

// sapling is the row this chapter gets wrong on purpose: a tree edited
// to survive in shade by every knob that sounds like it should help.
var sapling = terra.Species{
	Name: "sapling", Grammar: "sprig", Part: 14, Seg: 3,
	Form:  terra.Form{SegMass: 4, Fan: 5, Max: 4},
	Seed:  4.00,
	Catch: 0.25, Thirst: 0.60, Hunger: 0.15, Upkeep: 0.005, Root: 2.5,
	Reach: 3.5, Leafy: 0.060, Tall: 0.030,
}
$ go run ./cmd/flora -mode dim -ticks 400
flora: one plant of each row in 0.0500 units of light, nothing else standing

             seed  crown   leafy   income  ceiling    floor       t=1      t=50     t=200     t=400
  tree       2.00    2.5   0.030   0.0125    2.500   0.0400    2.0025    2.1108    2.3165    2.4327
  sapling    4.00    3.5   0.060   0.0125    2.500   0.0800    3.9925    3.6675    3.0504    2.7020

  in the open tree     settles at  600.000 grams and its crown lets 0.1429 of the light through
  in the open sapling  settles at  600.000 grams and its crown lets 0.0270 of the light through

Stood in five hundredths of a unit of light, the plain tree grows and the shade specialist shrinks, and they are heading for the same 2.500 grams from opposite directions. Four hundred ticks in, the row that was edited for shade weighs 2.7020 grams, having germinated at 4.00 and burned 1.2980 grams of tissue on the way down, while the row that was not edited at all weighs 2.4327 and is still climbing. The income column settles what happened: both rows earn 0.0125 units a tick, to the last digit.

Trace each edit to the code that reads it, using the figure at the top of the chapter. The crown radius is read by Canopy.Spread and Canopy.Pass, both of which are about the shade this plant throws on other plants. Leaf per gram is read by the same two. Neither of them appears anywhere in Plant.Limit, so neither of them can put a single unit of energy into this plant's own books. One number in the whole block decides what a plant earns out of a given amount of light, and it is Catch; one more decides how much of that earning stays standing, and it is Upkeep. Both were left exactly as they were. The seed mass is worse than useless here: it is read as the plant's starting mass, and upkeep is charged on mass, so doubling the seed doubles the light the seedling needs before it stops shrinking. The floor column says 0.0800 against the tree's 0.0400, and there is a whole band of light between those two figures where the ordinary tree lives and the shade specialist cannot.

The last two lines are the joke at the end. In the open, the two rows settle at the same 600 grams and the sapling's crown lets 0.0270 of the light through where the tree's lets 0.1429. The row that cannot live in shade throws the deepest shade in the table, passing a fifth as much light to whatever is standing under it.

The rule to carry out of this is not about plants. When behaviour is data, an edit is only as good as your knowledge of which code reads the field. Guessing from the field's name is how you get three careful edits that change nothing you wanted and one thing you did not. The check is mechanical and takes a minute: for each number you are about to move, find every method that reads it, and ask whether any of them is on the path to the outcome you are aiming at.

◆ Note — the sentence a row cannot say

A parameter block is a cheap and strong model and it has one hard edge, which this book will meet again. A row can only vary numbers the machinery already reads, so a species can differ from another in degree and never in kind. There is no row that fixes its own nitrogen, because there is no term for that in the ledger. There is no row that stores water in a swollen stem and lives off it through a dry spell, because the budget has one mass and no reserve. There is no row that drops its leaves when the light goes, no row with a taproot that reaches something the surface cells do not have, and no row whose seed carries a food supply, which is what the failure above was actually reaching for. Every one of those is a new effect, and a new effect is an edit to the machinery every plant in the valley runs, not a line in a table.

That is the trade the block is making and it is a good trade at this size. Four species cost four lines and no branches anywhere; a fifth costs one more line. The day a real new organ is wanted, the cost is a term in the ledger and a field in the block, paid once, and every existing row keeps working with a zero in it. Knowing which of the two kinds of change you are making before you start is most of the skill.

Parameters as behaviour

Strip the plants out and the pattern is one of the oldest in programming. There is a machine, written once, that reads its behaviour out of a record instead of having it written into its branches. Adding a kind of thing adds a record. The machine does not grow, does not learn a new case, and cannot be broken by the addition, because there is no if anywhere in it that mentions any particular kind.

Three properties make it hold up, and all three are visible above. The first is that the record is complete: every number that could make two individuals differ is in the block, so there is no second place to look when a plant behaves oddly. The second is that the record is copied on use. Germinate hands an individual its own numbers, so a plant that later changes can change without every other plant of its kind changing with it, and that property is the whole reason the block is useful to build rather than reading the table directly at every tick. The third is that the record is inert. It holds no pointers, no methods that surprise anybody, and no state, so it can be printed as a table, compared field by field, and written down.

The paper table earns a second mention. Every important number about these four rows was computed from the row before a tick was taken, and the runs agreed: 36, 72, 180 and 600 predicted, 36.0000, 72.0000, 180.0000 and 599.9735 delivered. That discipline is what makes the failure above findable in one run instead of an afternoon. When the shade specialist came out with an income of 0.0125 units, the same figure as the row it was supposed to improve on, there was a number to compare it against and a short list of fields that could have moved it.

And a row of numbers is a thing that can be copied. Copy it exactly and the copy is the same species; copy it with a small mistake in one field and the copy is a plant that is nearly its parent, which is a sentence with a great deal of the rest of this book folded into it.

✓ Checkpoint — a species you can write down
  • Handed a species row, you can name which method reads each of its fourteen numbers, and say which of them can and cannot change what that plant earns in a tick.
  • compute a row's open-ground ceiling, the generation its grammar can afford at that ceiling, and the two light figures it has to clear to stay alive and to branch, all before running anything.
  • Given the shade table, you can say which row is still a whole plant under two crowns and which is the last one alive under three, and defend both answers from the alive at and branches at columns.
  • explain why the scrub's digest is bit-for-bit identical on generous ground and on ground giving back a twelfth as much, and what that says about its strategy.
  • Shown a row edited for a purpose and behaving identically, check which methods read the fields that moved before check anything else.
  • list three things a real plant does that no filled-in row of this block can express, and say what it would cost to add one of them.
⚡ Exercises — try first, then reveal
Exercise 1 — take away half the daylight. Before running anything, work out what -light 6 does to each row's ceiling and which rows lose a generation from their picture. Then check with go run ./cmd/flora -mode row -light 6.

Income is light multiplied by the crown's share, so halving the light halves every income and every ceiling: 18, 36, 90 and 300. Now read those against the price lists. The moss keeps generation 2, which costs 1.60 against a ceiling of 18. The herb falls from 72 to 36, below the 50 grams generation 2 costs, so it drops to generation 1. The scrub falls from 180 to 90, below 160, so it drops from 3 to 2. The tree falls from 600 to 300, below 500, so it drops from 3 to 2 as well.

$ go run ./cmd/flora -mode row -light 6 | tail -5
          soil   income   ceiling    gen    tall    pass alive at branches at
  moss       4   0.3600     18.00      2    0.04  0.6400   0.0833      0.1333
  herb       6   1.8000     36.00      1    0.79  0.8000   0.1667      1.6667
  scrub     12   0.7200     90.00      2    1.08  0.8929   0.0333      0.6667
  tree      10   1.5000    300.00      2    9.00  0.5714   0.0400      0.4000

Three of the four rows look like a smaller kind of plant in half the light, and the one with the cheapest grammar comes through untouched. The two right-hand columns did not move at all, because neither of them mentions the light landing on the valley: they are both properties of the row, and the daylight is a property of the day.

Exercise 2 — find the rain that finally reaches the tree. Using the soil and income columns, work out the moisture and nutrient trickle at which the tree stops being limited by light, then run the four rows beneath it.

The tree's roots find ten soil cells, so a trickle of r moisture a cell pays for 10 × r ÷ 0.60 units of energy, and that stops covering the tree's 3.0000 when r drops below 0.18. The nutrient side is 10 × f ÷ 0.15, which stops covering 3.0000 below 0.045. Run it a shade under both.

$ go run ./cmd/flora -mode alone -ticks 2000 -rain 0.17 -feed 0.044
flora: one plant of each kind, each alone in its own 12x8 valley at 4,1
       12.00 of daylight, the ground putting back 0.17 of moisture and 0.044 of nutrient a cell a tick

               mass   height     leaf    gen  segments      limit            world
  moss      36.0000   0.0720   0.7200      2        16      light 05f1078fa6186607
  herb      22.6667   0.4987   1.1333      1         5      water 4d6a94beed3b4704
  scrub    180.0000   2.1600   4.5000      3        64      light 7c7adbc0c7bf6090
  tree     566.6427  16.9993  16.9993      3       125      water 7e3e4a7216807baa

The tree's limit reads water and it is heading for 10 × 0.17 ÷ 0.60 = 2.8333 of income over 0.005 of upkeep, or 566.67 grams, which is where 566.6427 is two thousand ticks in. It holds generation 3, because 566 is comfortably over the 500 that costs. The moss and the scrub come out untouched here, digest for digest. Only one of those two came through the deeper drought a few stages ago without moving a bit, and it is the one that spreads its roots over twelve cells and spends the least water per unit of energy it fixes.

Exercise 3 — draw them as they stand under one crown. Take the four ceilings from the one-crown row of the shade table and hand them back to the drawing. Predict which of the five digests from the earlier render will be unchanged, then run go run ./cmd/flora -mode shot -grams 5.143,10.286,25.714,85.714.

A row's picture depends on its generation and on nothing else about the mass, so any row whose generation is the same at both masses must produce the same digest. From the shade table, only the moss keeps generation 2 under one crown. Its digest should be the same 115643045c8a8fb1; the other three should all move, and so should the combined frame.

$ go run ./cmd/flora -mode shot -grams 5.143,10.286,25.714,85.714
flora: 4 rows on the bare frame, out of row 110
          grammar   part    seg      grams    gen   segments     w by h  its own frame
  moss        fan      5    1.0       5.14      2         16     4 by  4  115643045c8a8fb1
  herb      sprig      8    2.0      10.29      1          5     3 by  6  77864a6318a2aa81
  scrub       fan     12    2.5      25.71      1          4     2 by  5  0ee59165ca46c447
  tree      sprig     14    3.0      85.71      1          5     3 by  9  1fc621fc9b7e19a4
  the four together bd20113621b771e8febfd6a1ddb6bfb039f3c50076224c83bc40cde8ba7c592a

The moss is drawn to the pixel as it was at 36 grams, on a seventh of the tissue. The other three have collapsed into stubs of four or five strokes, and the tree that was twenty-seven pixels by eighty-one is three by nine: no wider than the herb beside it and three pixels taller. Understorey is a picture as much as it is a number, and the picture came out of the same three lines that drew the canopy.

Every plant above climbed to a size and stayed at it for however many ticks were left. Nothing here grows less at one time of year than at another, because there is no time of year: an open cell has received 12.00 units of daylight on every tick this volume has run and it will receive 12.00 on the ten millionth. That constant is doing more damage to these four rows than any of them can show. A plant that lives one summer and a plant that shuts down for a quarter of every year are not distinguishable in a valley where the sky never moves. Next: the year, as one angle gaining a fixed amount every tick, and read three ways.