Something That Eats the Dead
The second eater level
Every animal that has ever stood in The Hollow eats the same thing: a quarter of a gram from standing plant tissue, turned into energy while the body spends its store. A valley of animals like that has one row of eaters in it.
Two rules read the level and there are no others. A mouth at level one takes
standing tissue off a terra.Stand; a mouth above level one takes fallen mass
off a terra.Pile. A body may strike a living body of a strictly lower level.
That is the whole trophic web this book uses.
Counting rows is the whole idea. Plants are nought: nothing in this valley eats a plant's food, because a plant's food is light and water and minerals out of the ground. An animal that eats a plant is a one. An animal that eats an animal that eats a plant is a two.
The number counts the mouths between a body and the sunlight, and it is the number the last field of a creature row now holds.
The obvious way to build a two is to let something catch and kill a one, and that is expensive: a mouth on a living body, a rule about who may hit whom, and an effect at the far end. The cheaper two is already standing in the valley.
Everything that dies here falls onto the cell it was standing over as litter, and the litter lies there rotting away for the hundred or so ticks that takes. Nothing eats it. A creature that did would be a two without catching anything, because the grams in a carcass came off a plant through a browser's mouth on their way there.
The second of those two rules is already in the code and already refuses everything, because until this page there was nothing standing below anything. The first one is this chapter's, and the interesting thing about it is how little machinery it needs. The ground has had a way of putting mass onto a pile since the valley learned to rot: one function, called by everything that dies. It has never had a way of taking mass off one. A pile loses grams by rotting and by nothing else. So the second row's whole diet is one new function on the bed, and the mouth that calls it is the mouth that was already there, pointed somewhere else.
By the end of this page beast.Fauna has a second row in it with ten numbers
and a level. terra.Bed.Take exists and is the mirror of the fall. A genome
knows which row its ten factors are factors on, and the valley's grams ledger has a line
for what a mouth took off the litter and still closes to the last few bits.
The fallen bench ends with a run that the second row does not survive. It is
not a bug in the row or a mistake in the arithmetic: an animal living on what falls cannot
make a living in a valley where nothing kills anything, and the run says so in grams.
The creature level field
A creature row is ten numbers and a name. Every one of the ten is a price or a measurement: grams of body, energy units the store holds, what a gram costs to keep, what a unit of work costs, what one bite takes and what it is worth, how far a mouth reaches, how far an eye runs, how fast the legs go, how far one turn swings. A genome multiplies each of the ten by a factor of its own, and that is what makes the row a row and not a creature. The eleventh field is the level, and it is the only one that is not a quantity. It does not say how much of anything. It says who may do what to whom.
Choosing the ten numbers for a second row is the part that could go wrong quietly. Pick them by running the valley and nudging them until the population curve looks interesting and you have not built an animal, you have built a fitness function wearing a body: the run then proves whatever you tuned it to prove. So the second row is built out of one decision and arithmetic. The decision is that a body which stands over another body is not smaller than the body it stands over, and half again is the smallest step that means anything: sixty grams against forty.
Everything else follows or is copied. The store follows the body at the browser's own ten energy units a gram, six hundred against four hundred, and the mouth follows the body, 0.375 grams against 0.25. The other seven are the browser's numbers unedited, and each has a reason to be. A gram of animal costs a gram of animal to keep alive whatever the animal eats. A unit of movement work is a unit of movement work. A gram of tissue is worth what a gram of tissue is worth once it is inside, dead or standing, because a mouth does not know what it is eating. A mouth reaches a cell. Eyes cost ray steps every tick, so a row that saw further would be charging the phase for the privilege. The legs and the turn are the same under a heavier animal.
// internal/beast/beast.go
//
// The second row is the first row at half again the size, and every
// number in it either follows from that or is copied across unchanged.
var Fauna = []Kind{
{
Name: "browser",
Bulk: 40, Full: 400,
Basal: 0.004, Work: 0.60,
Bite: 0.25, Convert: 4.0, Reach: 1.0,
Sight: 12, Top: 0.45, Swing: 1.0 / 12,
Trophic: 1,
},
{
Name: "hunter",
Bulk: 60, Full: 600,
Basal: 0.004, Work: 0.60,
Bite: 0.375, Convert: 4.0, Reach: 1.0,
Sight: 12, Top: 0.45, Swing: 1.0 / 12,
Trophic: 2,
},
}
$ go run ./cmd/fallen -mode row
fallen: 2 rows in beast.Fauna, and one field that is not a price
field browser hunter why the second row holds what it holds
Bulk 40.0000 60.0000 half again the body
Full 400.0000 600.0000 ten units of store a gram, on both rows
Basal 0.0040 0.0040 a gram of animal costs a gram of animal
Work 0.6000 0.6000 a unit of work is a unit of work
Bite 0.2500 0.3750 a mouth in proportion to the body
Convert 4.0000 4.0000 a gram of tissue is worth what it is worth
Reach 1.0000 1.0000 a mouth reaches the cell it is standing on
Sight 12.0000 12.0000 the same eyes, at the same price in ray steps
Top 0.4500 0.4500 the same legs under a heavier animal
Swing 0.0833 0.0833 the same part of a turn
Trophic 1 2 where the row stands in the eating order
worked out from those ten, and set by nobody
the standing body costs a tick 0.1600 0.2400
ticks a full store covers 2500.0000 2500.0000
one mouthful is worth 1.0000 1.5000
ticks one mouthful pays for 6.2500 6.2500
a bite costs to take 0.1500 0.2250
a strike costs to throw 4.8600 7.2900
a child costs its parent 360.0000 540.0000
grams a full store is worth 100.0000 150.0000
the two rules that read Trophic, and there are no others
the mouth at level 1 it takes standing tissue off a stand,
above level 1 it takes fallen mass off a pile
the strike a body may strike a living body of a strictly lower level
what a founding animal of each row is born carrying
links, after the founding cut 144 168
genes in the whole genome 168 192
numbers one birth spends 512 584
what each row can ever take, off beast.Kind.Ever
browser rest, walk, sprint, turn left, turn right, bite
hunter rest, walk, sprint, turn left, turn right, bite, strike
The second block is the one to read twice, because two of its lines are equal and neither of them was made equal on purpose. Both rows fast for exactly 2,500 ticks on a full store, and both rows need a mouthful every 6.25 ticks. That falls out of the proportion: put the store and the mouth up by the same factor the body went up by, and the two ratios that decide how often an animal has to eat cancel the body out entirely. So the second row is not a harder animal to keep alive per gram than the first, and no result on this page can be blamed on it being given a worse body. What is different about it is what its mouth is allowed to point at, and nothing else.
Start with what a body costs to stand still. Four thousandths of an energy unit for every gram, sixty grams: 0.24 a tick. The store holds six hundred, so with nothing whatever coming in the animal has 600 divided by 0.24 ticks left, which is 2,500. Do the same for the browser and you get 400 divided by 0.16, which is also 2,500, because both the top and the bottom went up by one and a half.
Now what one mouthful buys. The mouth takes 0.375 grams and a gram of tissue is worth four energy units, so a mouthful is 1.5 units. Divide by the 0.24 a tick the standing body charges and one mouthful pays for 6.25 ticks. The browser's mouthful is 1.0 unit against a bill of 0.16, which is 6.25 ticks again. Eat more often than that and the store fills; eat less often and it empties. Neither row is told this number and neither row can read it: it is a fact about ten numbers, and an animal either finds a mouthful every six and a quarter ticks or it does not.
The bite itself costs something to take, and it is the movement charge this world prices everything by: the mass moved, times the square of the speed it moved at, at the row's price for a unit of work. A mouth carries 0.375 grams over one cell in one tick, so 0.60 times 0.375 times 1 times 1 is 0.225. Set that against the 1.5 the mouthful is worth and a bite returns nearly seven times what it costs to take. An animal standing on food therefore does well, and an animal walking about looking for it does not.
The same three lines in symbols, and then the ledger.
ticks a full store covers = Full ÷ (Basal × Bulk)
ticks one mouthful pays for = (Bite × Convert) ÷ (Basal × Bulk)
a bite costs = Work × Bite × Reach × Reach
The valley's grams ledger has had the same two sides since the terrarium was sealed. Everything that ever went in is what the valley opened with, plus every gram the leaves fixed out of light, plus the bodies the run was founded with. Everything that is now somewhere is standing plant tissue, walking creature bodies, seed in the air, seed in the bank, litter lying on the ground, grams a plant burned as upkeep, grams the rotting burned off the litter, grams that blew over the rim, and grams a mouth took off a stand. The two sides come to the same number, and the difference between them is printed rather than rounded away, because a residue you print is arithmetic and a residue you hide is a leak you have not found yet.
This chapter adds one line to the second side: grams a mouth took off the litter. One line, because there is exactly one new road. Mass that leaves a pile through a mouth was in the pile a tick ago and is in an animal's account now, and the line is where it goes so that the two sides still add up.
in = opened + built + stood
out = standing + walking + air + bank + lying + burned + rotted + blown + eaten off a stand + eaten off the litter
A second row raises a question the first one never could. A genome here is ten factors and
nothing else: 1.20 on the first factor means multiply the row's Bulk by 1.20.
With one row in the fauna there was no ambiguity about which Bulk. With two,
1.20 means forty-eight grams on one row and seventy-two on the other, and a genome that
did not say which row it belonged to would describe two different animals depending on who
was reading it.
So a genome gains a field naming its row, and the field is not a gene. A gene in this world is a number an operator may move: mutation perturbs it, a crossing takes it from one parent or the other, and the flat walk every operator uses steps over all of them in a fixed order. The row is none of those. It never mutates, never crosses, and never appears in the walk, because a genome that changed rows halfway through a life would be describing nothing at all. It is inherited whole, and it costs nothing to arrange: every child in this valley begins as a copy of a parent, and copying the struct copies the field.
That settles who may breed with whom, and it settles it by construction. Two creatures of different rows are not candidates for each other, and the pass says so before it works out one term of the compatibility distance the last volume installed. This matters more than it looks. The threshold that decides whether two animals of the same row are still the same species is a real measurement over two wirings and two bodies, and it would happily produce a number for a browser and a hunter. That number would mean nothing: the two genomes are not even the same length. The rows are separate because they are separate, and no chapter of this book may let a reader think a distance decided it.
// internal/gene/gene.go
type Genome struct {
Body [Bodies]float64
Mind []float64
Temper [Tempers]float64
Look [Looks]float64
Wire *mind.Graph
// Row is which entry of beast.Fauna the ten body factors are
// factors ON. It is not a gene and it is the only thing in this
// struct that is not: it never mutates, never crosses and never
// appears in the flat walk. Nought is the browser, which is what
// a genome made before this field existed says without being
// asked.
Row int
}
// Price is the one road from a genome to the row a creature is run
// off, and every animal in a valley goes down it, founder and child
// alike: the genome's own row out of the fauna, its ten factors
// stamped onto it, and then the bill for whatever its wiring listens
// to added to the Basal factor.
func Price(g *Genome, s Sense) beast.Kind {
k := g.Stamp(beast.Fauna[g.Row])
k.Basal *= s.toll(g.Wire)
return k
}
// internal/gene/sense.go — the founding takes the row's number now,
// because Shut wants the row itself and the genome wants the number.
func Root(r *rand.Rand, m *Mint, row int) *Genome {
g := Sprout(r, m)
g.Row = row
Shut(g, beast.Fauna[row])
return g
}
// internal/gene/birth.go — inside the search for a second parent,
// after the store is asked and before one term of the distance is.
if p.genes[o.ID].Row != mine.Row {
continue
}
$ go test ./internal/gene/ -run 'ARowIsNotAGene|PriceStampsTheGenomesOwnRow|TwoRowsDoNotBreed' -v
=== RUN TestARowIsNotAGene
--- PASS: TestARowIsNotAGene (0.00s)
=== RUN TestPriceStampsTheGenomesOwnRow
--- PASS: TestPriceStampsTheGenomesOwnRow (0.00s)
=== RUN TestTwoRowsDoNotBreed
--- PASS: TestTwoRowsDoNotBreed (0.00s)
PASS
ok theworld/internal/gene 0.003s
Price is one function and not two lines at every founding, and the reason
is a defect this valley has already paid for once. The last volume charged an animal for
the organs its wiring listens to, and it charged them in the breeding pass, which meant
that a creature the run was founded with was priced by a different arithmetic from every
creature born afterwards. It got away with it, because a founder's wiring listens to
nothing and its bill is therefore nothing. That is luck and not design, and a valley
whose first generation is priced differently from its second is a valley whose census is
measuring two animals and calling them one. One road, and every founder and every child
down it.
The third block is three lines and it is the whole of reproductive isolation between the rows. It sits above the compatibility check rather than beside it, so the distance is never even computed for a cross-row pair, and the run that founds both rows can say that no browser ever had a hunter offered to it as a mate. The last block of the row table says why that is the only sane answer: a founding browser carries 168 genes and a founding hunter carries 192, because the hunter keeps the nerves into an output the browser was founded without. Crossing two flat walks of different lengths would line gene 150 of one up against gene 150 of the other and hand the child a number off a link that does not exist in its own wiring.
Taking mass from a pile
The bed keeps three numbers for every cell of ground: moisture, nutrient, and what is lying on top waiting to go back in. The third one is a pile, and a pile is two numbers, grams of dead tissue and nutrient units still locked up in it. They are two numbers because they leave by two different roads. The grams go off as the rotting burns them. The nutrient goes down into the cell underneath. The rotting works the same share off each of them every tick and hands the second one to the soil, and that has been the whole life of a pile since the terrarium learned to compost.
There has never been a second road. Bed.Fall puts mass on a cell and is called
by every plant that stops standing and every animal that gets buried. Nothing anywhere
takes mass off. So the second row needs one function, and it is the exact mirror of the
fall: ask a cell for so many grams, get what actually left.
Two things about it are decided and not obvious. It never hands back more than the pile holds, for the same reason a bite never takes more tissue than is standing there: a mouth that took what was not there would be manufacturing grams, and this valley does not manufacture. And it moves the mass alone. A mouth may lift the grams and leave the minerals lying exactly where they are, because the minerals were never the mouth's to take. A plant borrowed them out of the bed the day it germinated and the pile is how they get back. A carcass has none in it at all: a browser repaid that loan on the tick it took the bite, when the grams it lifted off a plant sent their minerals straight into the ground under the plant.
A mouth that took the nutrient with the mass would be handing an animal units no animal ever drew out of the ground, which is the one book this valley has kept closed since the terrarium was sealed. It is easier to break than it looks, and the worked failure below breaks it.
// internal/terra/bed.go
// Fall drops dead tissue on one cell. Nothing is capped and nothing is
// refused: what a pile holds is already in the world's books.
func (b *Bed) Fall(c sim.Coord, p Pile) {
if !b.In(c) {
return
}
l := &b.litter[b.index(c)]
l.Mass += p.Mass
l.Nutrient += p.Nutrient
}
// Take lifts mass off the pile on one cell and hands back the grams
// that actually left it. It is the mirror of Fall and it is the only
// way anything anywhere takes mass off the litter.
func (b *Bed) Take(c sim.Coord, grams float64) float64 {
if !b.In(c) || grams <= 0 {
return 0
}
l := &b.litter[b.index(c)]
if grams > l.Mass {
grams = l.Mass
}
if b.Thorough && l.Mass > 0 {
l.Nutrient -= l.Nutrient * (grams / l.Mass)
}
l.Mass -= grams
return grams
}
$ go run ./cmd/fallen -mode take
fallen: one cell, one pile, and a mouth that keeps coming back
Bed.Fall put 1.200000 grams and 0.900000 nutrient units on 1,1
a hunter asks Bed.Take for 0.375000 grams at a time
bite asked got mass left nutrient left
1 0.375000 0.375000 0.825000 0.900000
2 0.375000 0.375000 0.450000 0.900000
3 0.375000 0.375000 0.075000 0.900000
4 0.375000 0.075000 0.000000 0.900000
5 0.375000 0.000000 0.000000 0.900000
mouthful 4 got less than it asked for, because a pile hands over
what it is holding and never more, and mouthful 5 got nothing at
all off a cell with nothing left on it
the nutrient column has not moved once: 0.900000 units are still
lying on 1,1, and terra.Bed.Rot will hand them to the cell
underneath on the schedule they were already on
$ go test ./internal/terra/ -run 'TakeIsTheMirrorOfFall|TakeRefusesWhatIsNotThere' -v
=== RUN TestTakeIsTheMirrorOfFall
--- PASS: TestTakeIsTheMirrorOfFall (0.00s)
=== RUN TestTakeRefusesWhatIsNotThere
--- PASS: TestTakeRefusesWhatIsNotThere (0.00s)
PASS
ok theworld/internal/terra 0.002s
The fourth mouthful is the interesting line and the reason the function hands back a number instead of a yes. The mouth asked for 0.375 grams off a pile holding 0.075 and got 0.075, and the caller has to know that, because what it turns into energy is what actually left and not what it asked for. Every function in this valley that moves mass is written this way, and the discipline has one payoff and it is the ledger: a total that is added up out of what really moved cannot drift away from a total added up out of what is really there.
Now the mouth. The bite is the sixth entry of the action table and it has not changed: same name, same two ticks, same price, same precondition in words. What changed is the question the precondition asks, and it asks it off one number. A mouth at level one looks for the richest stand inside its reach, which is the search it has always run. A mouth above level one looks for the cell inside its reach with the most lying on it, which is the same loop pointed at piles.
Both roads end in the same refusal value, and that is decided rather than lazy. The check has four ways to say no, and one of them has always meant this mouth has nothing it can eat inside its reach. A caller asking whether that is what happened asks by value, and it gets the same answer for both rows. The English sentence hung on the value talks about standing tissue, because it was written in a valley where every mouth was a mouth on a plant. The sentence is English. The value is the rule, and the rule did not change.
// internal/beast/act.go
// Scraps is the cell this creature's mouth can get to that is holding
// the most fallen mass, and how many grams are lying on it. It is
// Meal's own loop pointed at piles instead of at stands, and it keeps
// every one of Meal's rules.
func (b *Beast) Scraps(v *View) (sim.Coord, float64) {
n := int(b.Kind.Reach)
here := b.Cell()
best, most := here, 0.0
for dy := -n; dy <= n; dy++ {
for dx := -n; dx <= n; dx++ {
c := here.Offset(dx, dy)
if !v.Valley.Bed.In(c) {
continue
}
if m := v.Valley.Bed.Litter(c).Mass; m > most {
best, most = c, m
}
}
}
return best, most
}
// mouth refuses a bite with nothing to bite, and which question it
// asks is decided by one number on the row.
func mouth(b *Beast, v *View) error {
if b.Kind.Trophic > 1 {
if _, g := b.Scraps(v); g <= 0 {
return ErrNoReach
}
return nil
}
if st, _ := b.Meal(v); st == nil {
return ErrNoReach
}
return nil
}
// internal/beast/body.go — Graze's mirror. Grazing hands the bed a
// pile of pure nutrient, because a browser owes the ground the
// minerals in every gram it takes off a living plant. Scavenging
// hands the bed nothing: those minerals are already lying on it.
func (b *Beast) Scavenge(c sim.Coord, v *terra.Valley) float64 {
grams := v.Bed.Take(c, b.Kind.Bite)
v.Eaten += grams
b.Ate += grams
return grams
}
$ go run ./cmd/fallen -mode bite
fallen: 16x12 valley, tick 901, year 1 summer
the cell holding the most litter is 12,10 at 0.952644 grams
the whole valley is holding 0.952644 grams of it on 1 cells
creature 1, a browser at level 1, standing on 12,10
standing tissue inside reach 117.295648 grams on 12,9
fallen mass inside reach 0.952644 grams on 12,10
the check says allowed
grams off a stand 0.250000
grams off a pile 0.000000
energy those grams were worth 1.000000
the bite itself charged 0.150000
creature 2, a hunter at level 2, standing on 12,10
standing tissue inside reach 117.045648 grams on 12,9
fallen mass inside reach 0.952644 grams on 12,10
the check says allowed
grams off a stand 0.000000
grams off a pile 0.375000
energy those grams were worth 1.500000
the bite itself charged 0.225000
the pile on 12,10 is down to 0.577644 grams and its 0.094650 nutrient
units have not moved. terra.Valley.Eaten is 0.375000
$ go test ./internal/beast/ -run 'TheSecondRowIsTheFirstRowScaled|OneMouthTwoRoads|BothRowsAreRefusedByOneValue' -v
=== RUN TestTheSecondRowIsTheFirstRowScaled
--- PASS: TestTheSecondRowIsTheFirstRowScaled (0.00s)
=== RUN TestOneMouthTwoRoads
--- PASS: TestOneMouthTwoRoads (0.01s)
=== RUN TestBothRowsAreRefusedByOneValue
--- PASS: TestBothRowsAreRefusedByOneValue (0.01s)
PASS
ok theworld/internal/beast 0.022s
Two animals standing on the same cell of the same valley on the same tick, both allowed a bite, both taking one, and the two of them touching different things. The browser can see the 0.95 grams lying under its feet as clearly as the hunter can and takes its quarter of a gram off the plant next door. The hunter can see the 117 grams of standing tissue a cell away and takes 0.375 off the ground. Neither of them chose. The row decided, at the rule, before the effect ran, and the second line of each pair is the one that proves it: each animal reports what it found on both roads and then goes down one of them.
The last line is the ledger's new column doing its first work. 0.375 grams left a pile and 0.375 grams are on the valley's count of what a mouth took off the litter, and the 0.09465 nutrient units that were lying on that cell are still lying on it.
The hunter run
Everything is now in place for a valley with two levels in it: a row that eats plants, a row that eats what falls, a bed that will hand mass over, a genome that knows which row it is on, and a ledger with a line for the new road. So found one. Twenty-one browsers off the stream browsers have come off since the creature chapters, eight hunters off a stream of their own, all of them driven by wirings that grow their own nodes, sixteen years of valley, and nothing anywhere in it able to kill anything.
The hunters get a stream of their own because they are founded after the browsers and read a generator no browser touches. Every cell a browser lands on is a cell it would have landed on in a valley with no second row in it at all. Putting hunters into a valley does not move one browser. That is the only new source of randomness this whole page adds.
// cmd/fallen/valley.go — the ledger's new line, and the total it goes into
in := v.Opened + v.Built + r.Stood
out := standingMass + r.Walking() + alofting + waiting + lying.Mass +
v.Burned + v.Rotted + v.Blown + r.Ate + v.Eaten
$ go run ./cmd/fallen -mode valley
fallen: 16x12 valley, 21 browsers off stream 12 and 8 hunters off stream 22,
every one of them driven by a graph the table's own width, and nothing
in this valley able to kill anything
year browsers hunters standing lying off the litter
1 21 8 4654.10 0.95 0.00
2 12 0 6465.35 146.27 30.23
3 29 0 7287.30 157.60 30.23
5 20 0 7348.02 566.15 30.23
9 232 0 5255.79 644.26 30.23
13 338 0 3693.90 110.14 30.23
17 333 0 3647.68 98.98 30.23
29 founded, 1185 born, 881 struck off, 333 still walking after 57150 ticks
the last hunter was struck off on tick 2353, 1452 ticks after the valley
was founded, and 16 of the 16 that ever lived went the one way
anything in this valley has ever gone
the two rows' accounts, added up over every animal that ever lived
browsers hunters
animals that ever lived 1198 16
creature-ticks they were stepped 11610914 5437
grams their mouths took 772389.10 30.23
energy that came in 2989867.19 120.93
energy standing still charged 836719.75 1272.86
income less rent 2153147.44 -1151.93
income as a share of rent 357.33% 9.50%
Sixteen hunters ever lived and not one of them saw the end of the valley's second year. The eight founders opened with full stores, which covers 2,500 ticks of doing nothing at all, and the last of them was struck off 1,452 ticks after the founding. They did not starve slowly. They starved faster than an animal that had never eaten anything would have starved, because a full store is not the only thing a founder spends: at six hundred units it can afford a child at five hundred and forty on the first breeding pass it is asked, so eight founders immediately became eight founders on sixty units apiece and eight newborns on three hundred. Browsers do exactly the same thing at exactly the same ratio, and the browsers survived it, because a browser standing anywhere in this valley is a few ticks' walk from a plant.
The bottom four lines of that block are the finding, and they are arithmetic rather than a story. Rent is what a standing body charges its own store every tick, before a single action on the table is priced: the animal is asleep, it is not moving, it is not eating, and the bill still arrives. Over every hunter that ever lived, that bill came to 1,272.86 energy units. Everything their mouths brought in over the same 5,437 creature-ticks came to 120.93. Nine and a half percent. A hunter in this valley could not pay for the privilege of standing still.
Put the browsers' column beside it. Their mouths brought in three and a half times what standing still cost them, which is the margin that pays for walking, for turning, for biting and for children, and it is why there are 333 of them at the end and 21 at the start. Same bill per gram, same mouthful per tick of hunger, same eyes, same legs. The difference is entirely in what the two rows were able to find.
$ go run ./cmd/fallen -mode valley | tail -43 | head -17
where the litter went, in grams, over the whole run
fell onto it in all 44521.51
of that, carcasses 25725.23
and dead plant tissue 18796.29
the rotting burned off 44392.30
a mouth took off it 30.23
still lying there 98.98
the share a mouth got 0.07%
grams the hunters' rent alone came to 318.21
the seventh entry, in a valley where it lands on nothing
strikes the hunters were handed 147
energy they paid to throw them 1136.27
against their rent 89.27%
income as a share of rent, without them 9.50%
This is the part that is not what you would guess. The valley was not short of dead things. Forty-four and a half thousand grams fell onto the litter over sixteen years, twenty-five thousand of them carcasses of starved browsers. The hunters needed 318 grams to cover the rent of all the lives they had. They got thirty. Seven hundredths of one percent of everything that fell went into a mouth, the rotting burned off 99.7, and the last two tenths of a percent are still lying on the ground where they fell.
So a scavenger fails here for a reason that has nothing to do with how much dies. A pile is a race, and the competitor has every advantage. The rotting works on every cell of the valley on every tick. A hunter works on one cell, and only on the ticks it happens to be standing on that cell, and only until the pile under it is gone, which for a browser-sized carcass is a hundred and seven mouthfuls of a hunter's jaw and a good deal longer than the pile lasts. There is no smell in this world that carries: an animal reads how much is lying on the cell underfoot and how that reading slopes to the east and to the south, and those three numbers tell it nothing at all about a carcass four cells away. It has to walk onto the food by accident.
The second block is the one where honesty costs something, so read it plainly. The hunters
are the first animals in this world that are allowed the seventh entry, and they took it
147 times, for 1,136.27 units between them, which is 89 percent of what standing
still cost them. The row's own price for a strike is 7.29 and not one animal in the run
paid exactly that: every creature here is stamped from a genome that moved its
Work, its Bulk and its Top, and 1,136.27 over 147
throws is an average of 7.73. A strike in this valley is priced, refusable and lands on nothing: no gram
moves and no store but the striker's own changes, and the bench holds every animal in
the run to that by setting Feinting on every body it stands up. So those 147 strikes did make the
hunters' deaths faster, and it would be dishonest to leave that out. It is also not the
finding, and the last line of the block says why. Rent is charged before any action is
priced. Take every strike off the row and the income is still 9.50 percent of a bill the
animal cannot avoid. A hunter that never threw one would have starved on the same argument
a little later.
The mistake is four characters long and it is the most natural one on the page.
Fall takes a pile and adds both of its numbers to the cell. Take
is described everywhere as the mirror of Fall. A mirror of a function that
moves two numbers moves two numbers, so the mouth lifts the pile's share of nutrient out
along with the mass.
// internal/terra/bed.go — the mouth taking both of a pile's numbers
if grams > l.Mass {
grams = l.Mass
}
l.Nutrient -= l.Nutrient * (grams / l.Mass) // the mirror, taken too far
l.Mass -= grams
Now run the valley and check the books. The grams close. They close perfectly, because nothing about grams changed: the mass left the pile and arrived in the count of what a mouth took off the litter, and both sides of the ledger moved by the same amount. Every test in the module passes. The bite is right, the refusal is right, the population is the population. The defect is in the other set of books, the one this chapter does not touch and the one no line of this chapter appears in.
$ go run ./cmd/fallen -mode dirt -thorough
fallen: the nutrient books after 4 years, with 8 hunters eating off the litter
and a mouth that lifts the minerals out with the mass
the valley opened with 1632.000000 a full cell of soil, a quarter of one in a crack
in living tissue 439.993587 drawn out of the ground and still standing
in the air 0.000000 locked up in seed still flying
in the bank 268.330000 locked up in seed waiting in the ground
lying in the litter 589.461782 dead tissue that has not rotted down yet
in the soil 325.890946 back in the cells, ready to be drawn again
blown away 0.000000 left over the rim inside a seed
out 1623.676314
difference 8.323686 8.324e+00
eaten off the litter 30.232266 grams a mouth took off the litter
the grams residue -0.000000 -1.496e-08, and every gram is where it should be
Eight and a third nutrient units have gone. Not moved somewhere the audit forgot to look: gone. There are six places a unit can be in this valley and the run adds up all six, and the total is 8.32 short of what the valley was founded holding. Set that beside the last line of the same run, where the grams residue is fifteen billionths of a gram over a couple of million, and the pattern is unmistakable: one ledger closes to the last bits and the other one is out by a round number. A rounding error is small and ugly. A missing eight point three two three six eight six is neither.
The road from that pattern back to the cause is short. The two books share exactly one thing that changed on this page, so the leak has to be there, and the arithmetic confirms it: the mouths took 30.23 grams, the piles they took them off were plant litter carrying minerals at the rate a plant carries them, and 8.32 units is what those grams were holding. Take the flag off and run it again and the difference goes to -1.728e-11.
The general lesson is about which audit catches a bug. The line this chapter wrote is a grams line, so a careful author writes a grams check, and that check would have passed on every run of the broken version forever. What caught it was the audit over the resource nothing on this page was supposed to touch.
Then the grams themselves. The 30.23 the hunters took off the litter were lying on the ground a moment before and are in an animal's account now, and without a line for them the ledger's second side would be short by exactly that much and would report a leak the valley does not have.
$ go run ./cmd/fallen -mode valley | tail -20 | head -17
the books in grams
the valley opened with 67.000000 plants founded on the first tick
built out of light 1915023.020597 every gram the leaves ever fixed
creatures stood up 31465.206947 bodies the run opened with
in 1946555.227544
standing 3647.676464 living plant tissue
walking 5739.980238 living creature bodies
in the air 0.000000 seeds still flying
in the bank 2047.500000 seeds waiting in the ground
lying dead 98.984924 litter: plants and carcasses together
burned as upkeep 1118209.459967 grams a plant spent on standing there
rotted 44392.295974 grams the litter has burned off
blown away 0.000000 seeds that left over the rim
eaten off a stand 772389.097701 grams a mouth took off living tissue
eaten off the litter 30.232266 grams a mouth took off a pile
out 1946555.227535
One million nine hundred and forty-six thousand grams in, the same number out, and a difference of nine millionths of a gram which is printed because it is the last bits of adding a million numbers in a written-down order and not because anybody is comfortable with it. The two lines at the bottom of the second column are the two mouths in this valley, and the second of them is new. Everything else on the page is exactly where it was.
That run ends on a line the blocks above leave out: 57,150 ticks in 25.755 seconds, 2,219 ticks a second, measured on an eight-core Ryzen 7 3700X and different on your machine. The tick count is arithmetic and is compared like every other number here. The duration and the rate are this machine talking, and no argument on this page rests on either.
Why levels choose food roads
Take the valley away and what is left is a way of adding a category to a system without adding a mechanism to it. There was one kind of eater and one road from food to a store. The second kind of eater is one integer on a row and two places that read it, and one of those places already existed. Nothing about the tick got slower, nothing about the phase moved, and no code anywhere asks what kind of animal it is holding.
The move that makes this possible is putting the difference on the road and not in the
type. A second creature struct with its own methods, a diet field with a switch over three
named diets, an interface with a Feed method and two implementations: every
one of those makes the number of kinds of animal a fact about the source code, and each
new kind is then a file. Putting it on a road makes it a fact about a slice. An eleventh
row of the fauna at level 4 would need no new code at all, would be refused a strike
against anything at level 4 or above, and would eat off piles like every other row above
one. The rule is a comparison, and comparisons do not care how many things there are.
The second general thing is about mirrors and about how far to trust one. Take
is described as the mirror of Fall and it is, for one of the two numbers a
pile holds. The failure above is what happens when a symmetry that is true of the interface
is assumed to be true of the meaning. Grams and minerals go into a pile together because
they were together in the thing that died. They come out separately because they are going
to different places: the grams are food and the minerals are a debt to the ground. A
function that is the mirror of another function in its signature is not necessarily the
mirror of it in what it means, and the way you find out is to audit the quantity the new
road was not supposed to touch.
And the third is the one the run paid for. A budget is the honest way to ask whether an animal can live somewhere, and the two numbers you want are what it takes in and what it is charged before it does anything at all. Population curves are downstream of that and easy to read wrongly: a row can be dying because it cannot find food, because it spends too much moving, or because its children cost more than it can carry, and the curve looks the same in all three cases. The rent line separates them. A row whose income is under its rent cannot be saved by better behaviour, because behaviour is what the rest of the budget pays for. That is the whole diagnosis here, and it is why this page can say what it says about a scavenger without tuning a number to make it come out.
- Say what number a plant, a browser and a hunter each stand at in the eating order, and why plants are not creatures even though something eats them.
- Work out, in your head, how long a full store keeps a hunter alive with nothing coming in, and say why the answer is the same for a browser whose body is two thirds the size.
- Explain why
Bed.Takehands back a number instead of a yes, and what breaks if a caller turns what it asked for into energy instead of what it got. - Given a genome carrying the factor 1.20 on
Bulk, say how many grams of body it describes, and say what extra piece of information you had to be given before you could answer. - Name the one column the valley's nutrient books gained on this page, and check your answer against the fact that the grams books gained exactly one line.
- Given a run where the hunters' income is nine and a half percent of their rent, say whether a better controller could have saved them, and say what number in the block you are reading to decide.
Exercise 1 — put the hunters on the plants. Change
Fauna[1].Trophic from 2 to 1 and run the valley again. Predict what
happens to the population, to the litter line and to the ledger before you look.
The hunters become large browsers, and large browsers do fine: their mouths are half again as big and their bills are half again as big, so the rhythm that decides whether an animal keeps up is the same 6.25 ticks it always was, and there is a plant within a few ticks' walk of anywhere. The population line stops going to nought. The litter line stops moving at all, because nothing above level one is left in the valley and the litter road belongs to the level and not to the row.
The ledger still closes, and the line that changes is not the one most people guess.
Eaten off a stand goes up, because there are more mouths on the plants;
eaten off the litter stays at 0.000000 for the whole run. Two of the pinned
tests fail and both name the cause without any hunting:
go test ./internal/beast/ -run TestTheSecondRowIsTheFirstRowScaled
reports that the rows stand at 1 and 1, and
TestOneMouthTwoRoads reports that the hunter took 0 off a pile and
0.375 off a stand. Put the 2 back.
Exercise 2 — slow the rotting down. The run says the rotting burned off 99.7 percent of everything that fell. Find out how much of the hunters' problem that is by running the valley with the litter rotting ten times more slowly, and say before you run it whether you expect them to survive.
Change v.Decay in cmd/fallen/main.go from 0.01 to 0.001
and run -mode valley. A pile now takes about ten times as long to burn
away, so there is roughly ten times as much lying on the ground at any moment for a
mouth to walk onto.
It buys the hunters more than twice the grams and it does not buy them enough: their income goes from nine and a half percent of their rent to about a quarter of it, and the last of them is still struck off inside the first year. The reason is in the arithmetic they cannot get round. They need a mouthful every 6.25 ticks, a mouthful comes off one cell, and nothing in the sensor row tells an animal where a pile is unless it is standing on it or beside it. More litter raises the chance of walking onto some; it does not turn walking onto some into a rhythm. Watch the income as a share of rent line and not the population: that is the number that says whether a row could ever pay its own way, and it is the number to quote when you report what you found.
Two honest warnings, and the first is worse than it sounds. Slowing the rotting slows the minerals going back into the soil, the plants draw on that soil, and a valley whose plants are short is a valley whose browsers starve as well: run it out to year nine and there is nothing walking in it at all. You have not run a controlled experiment on the hunters, you have run a different valley. Every number in every other run on this page moves with it, from tick one. Put the 0.01 back before you compare anything with anything.
Exercise 3 — audit a road you invent yourself. Write a test that puts a known pile on a known cell, takes a known number of mouthfuls off it, and asserts that the two books both close. Then break it on purpose and watch which assertion fires.
The pattern is the one the worked failure turns on. Write down what went in, do the thing, add up everywhere a unit can be, and compare. Two totals, not one:
bed := terra.NewBed(sim.Generate(4, 4, 5), terra.Cell{Moisture: 1.25, Nutrient: 12})
at := sim.Coord{X: 1, Y: 1}
bed.Fall(at, terra.Pile{Mass: 1.0, Nutrient: 0.4})
took := 0.0
for i := 0; i < 3; i++ {
took += bed.Take(at, 0.25)
}
// the grams: what fell is what is lying there plus what left
if got := bed.Litter(at).Mass + took; got != 1.0 {
t.Errorf("the grams come to %v, want 1.0", got)
}
// the minerals: nothing on this road may touch them
if got := bed.Litter(at).Nutrient; got != 0.4 {
t.Errorf("the minerals come to %v, want 0.4", got)
}
Set bed.Thorough = true between the fall and the loop and run it again.
The first assertion passes and the second one fails with 0.10000000000000002 against
0.4, which is 0.75 of the minerals gone with 0.75 of the mass. That is the whole
worked failure in six lines, and it is the test that would have caught it on the day
it was written. Write the second assertion first next time: the audit that catches a
new road is the one over the quantity the road was not supposed to move.
The valley now has two levels in it and one of them is empty. The row exists, its mouth works, its ledger line balances, and a run of sixteen years says that an animal waiting for something to fall gets a thirtieth of what it needs to stand still. What it never gets to do is any of the deciding. A carcass in this valley arrives when a browser's store closes a tick at nothing, somewhere the hunter was not, and by the time anything walks over it the rotting has had a hundred ticks at it. The seventh entry is sitting on the table, priced and refusable and taken 147 times to no effect whatever, and what it needs is for something to happen at the far end of it.