Three Genes Find a Reader
Aggression, wariness and tameness
A genome in The Hollow holds four blocks, and thirteen of its numbers are not part of the controller. Ten price the body. Three are temperament: aggression, wariness and tameness.
Aggression multiplies both ends of a strike by one and the same number,
1 + Fury × aggression: the price the striker's muscles are charged and
the blow the struck body takes. Wariness divides the blow, so the body on the far end takes
blow × (1 − Ward × wariness), and it is charged every tick
of that animal's life as a surcharge on its Basal factor,
1 + Watch × wariness, in the same place and by the same arithmetic as
the bill for a sense organ. Tameness is read by nothing.
The ten body numbers say how many grams an animal is, what a gram costs to keep, how wide a mouthful is, and how far the legs carry it. Every one is read on every tick of every animal's life, and an animal whose ten are badly chosen puts less in its store than it takes out and ends as a pile on a cell.
The temperament numbers were copied at every birth, awarded to one parent or the other at every crossing, nudged and replaced by the same mutation pass at the same rates, held inside the same range by the same clamp, and written into the archive on the line every born creature gets. Before this page, not one line of code in this world read one of them.
The volume that put them in the genome measured them wandering at exactly the rate a number wanders when nothing whatever is pushing it. It also gave the reason they were there at all: a record of who came from whom cannot be given a new column once the ancestors it would have described are dead and gone.
The seventh entry of the action table now lands, and a landing blow is the first event in this world where one creature's numbers meet another creature's numbers. A strike has two ends: the end the striker pays for, which is one multiplication over three numbers off its own row, and the end that arrives in somebody else's store, which is the same multiplication read a second time.
That is not two readers of one kind. A weapon is paid for when it is swung and armour is paid for whether or not anybody comes, and the difference between those two ways of charging for a thing is the argument this page is here to make. It decides which of the two genes a valley can be expected to have an opinion about, and the margin turns out not to be close.
By the end of the page there is a bench called temper. There are three
constants with a defence in numbers apiece. The blow is read off two genomes at the moment
it lands, the standing charge is folded into the one function that prices a genome into a
row, and the herd that has closed on the same grams total since before there were genomes
still closes on it.
The final measurement says what sixteen years do to three genes when two of them have a reader, held against the law the last volume derived for a gene that has none.
The strike and toll readers
Start with where the code may go, because in this valley that is a short list and it
settles most of the design. Package beast prices an animal and knows nothing
at all about genomes; it has never seen one and must not start now. Package
gene knows everything about genomes and is not allowed inside the arithmetic
of a tick. A blow happens inside a tick, in beast, and the numbers it needs
are facts about a genome, in gene.
The way across is the way the wiring's own bill went across. A body already carries a row
of numbers that were computed from its genome once, when it was stood up, and never
consulted again: that is what beast.Kind is. Wariness has a standing charge,
the standing charge is a multiplier on Basal, and Basal is on
that row already. So the surcharge needs no new field anywhere. It is one more
multiplication at the moment a genome is priced.
The blow is different. A price is settled once and carried for a lifetime; the two numbers a blow reads are pulled off two animals at the instant a strike lands, and neither of them is a fact about the kind of creature either animal is. Those get two fields on the body itself, written by whatever priced it, read at one place in the module and nowhere else.
// internal/gene/temper.go
// Temper is how one valley reads the three temperament genes: what
// aggression does to a strike, what wariness turns aside of one, and
// what wariness costs a body every tick of its life.
//
// It is off at the zero value, and off here means off by arithmetic
// rather than by a flag. Every one of the three numbers is a share of
// something, all three are nought in a Temper nobody filled in, and a
// creature priced under one is priced exactly as this valley priced
// creatures before a gene decided anything.
type Temper struct {
Fury float64 // what aggression is worth at the top of its range
Ward float64 // what wariness turns aside at the top of its range
Watch float64 // what wariness adds to Basal at the top of its range
}
// Read is the rule every valley in this book that reads a temperament
// reads it under.
var Read = Temper{Fury: 1.00, Ward: 0.50, Watch: 0.07}
// Throw is what this genome's aggression adds to a strike, as a share
// of the row's own price rather than as a multiplier: nought for an
// animal at the bottom of the range and Fury for one at the top.
//
// A share and not a multiplier, because a body nothing has priced a
// temperament onto holds nought and is charged the row's price
// exactly, and that is what every run made before this reading existed
// is entitled to.
func (t Temper) Throw(g *Genome) float64 { return t.Fury * g.Temper[Aggression] }
// Guard is the share of a blow this genome's wariness turns aside. The
// gene is clamped into 0 to 1 and Ward is under one, so the share is
// under one however the gene drifts and a blow always arrives.
func (t Temper) Guard(g *Genome) float64 { return t.Ward * g.Temper[Wariness] }
// Fit writes onto one standing body the two numbers a strike reads.
func (t Temper) Fit(b *beast.Beast, g *Genome) {
b.Fury, b.Ward = t.Throw(g), t.Guard(g)
}
// internal/beast/body.go — two fields on a creature, and shares rather
// than factors, so a body nothing priced a temperament onto reads
// nought for each and takes the row's own blow whole.
Fury float64
Ward float64
// internal/beast/act.go — inside apply, where the seventh entry has
// charged its price and landed its blow since the page that wrote it
case Strike:
throw := k.Lunge() * (1 + b.Fury)
t.Price = throw
if q, _ := b.Quarry(v); q != nil && !b.Feinting {
t.Blow = throw * (1 - q.Ward)
q.Store -= t.Blow
q.Hurt += t.Blow
}
b.aim(0)
Four lines of arithmetic, and the interesting one is the third. The striker's own share
is applied to throw, which is then charged to its store and sent
down the line, so both ends of the strike go up together. The struck body's share is
applied after the striker has been charged, so it comes off what arrives and off nothing
the striker pays. A fierce animal that swings at a wary one burns the whole swing and
delivers a fraction of it, and no line anywhere gives it that fraction back.
One consequence of putting the factor here rather than on the table is worth
naming, because the page that built the seventh entry said the opposite in good faith.
The check asks whether the store can pay Table[Strike].Price, and that is
the row's own Lunge with no temperament anywhere in it; four lines later
apply charges Lunge times one plus Fury. So an
animal holding somewhere between the two is allowed a throw it cannot afford, spends
past nothing, and dies of the one thing everything in this valley dies of. For the other
six entries the number the check tests and the number the tick charges are one number;
for the seventh they are two, and the reason is not an oversight. A
Move.Price is handed a beast.Kind, a Kind is a
row and not an animal, and a row does not have a temper. Giving the table the creature
instead would change the shape of all seven entries to pay for one.
$ go run ./cmd/temper -mode blow
temper: one hunter on 5,4, one browser on 6,4, and the two ends
of one blow
the row's own strike costs 7.2900 and lands 7.2900, which is the one
multiplication the seventh entry was priced with charged to two stores.
Fury is 1.00, Ward is 0.50 and Watch is 0.07
what the striker's own gene does, against a browser at wariness 0.00
aggression paid landed blows ticks spent
0.00 7.2900 7.2900 55 109 400.9500
0.25 9.1125 9.1125 44 87 400.9500
0.50 10.9350 10.9350 37 73 404.5950
0.75 12.7575 12.7575 32 63 408.2400
1.00 14.5800 14.5800 28 55 408.2400
the last column hardly moves and the two beside it halve. Both ends of
the throw are one number, so a fiercer animal buys ticks and not energy
what the struck body's own gene does, against a hunter at aggression 0.00
wariness paid landed blows ticks spent
0.00 7.2900 7.2900 55 109 400.9500
0.25 7.2900 6.3788 63 125 459.2700
0.50 7.2900 5.4675 74 147 539.4600
0.75 7.2900 4.5563 88 175 641.5200
1.00 7.2900 3.6450 110 219 801.9000
here the paid column stands still and the last one climbs. The share
comes off what arrives and off nothing the striker is charged, so a
wary animal makes emptying its store of 400 cost more than 400
Two tables, two entirely different kinds of purchase. Read the top one along the ticks column: an animal at the bottom of the aggression range needs a hundred and nine ticks of standing next to its prey to empty a full store, and one at the top needs fifty-five. It has bought half the exposure for almost exactly the same energy, because the strike chapter's identity survives multiplication: the blow is the price, and multiplying both by one number leaves them equal. What a kill costs is the store you are emptying, whatever you are built like.
The bottom table breaks that identity on purpose, and it is the first thing in this world that does. The paid column does not move at all. The landed column halves. So the bill for a kill goes from four hundred energy units to eight hundred, and the extra four hundred is not spent on anything: it is thrown, it is burned in a hunter's legs, and it arrives nowhere. That is what armour is. It does not save its owner anything it can spend; it makes the attempt cost the other animal more than it is buying.
Then the other bill, the one wariness carries whether or not a hunter ever walks past. It
is a surcharge on Basal, which is the energy one gram of body charges its own
store every tick, and the reason it goes there rather than anywhere else is the reason a
sense organ's bill goes there: it is the one number no creature can dodge by resting,
hiding, or being lucky with the weather.
// internal/gene/gene.go
// 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.
//
// Four steps in a fixed order. The genome's own row is looked up in
// beast.Fauna and the ten factors are stamped onto it. Then the bill
// for whatever the wiring listens to is added to the Basal factor,
// because an ear is an organ and an organ has to be kept alive. Then
// what this valley charges for wariness, which is the same shape of
// bill for the same kind of reason. What comes out is the row that one
// particular animal is priced by for the whole of its life.
//
// The tolls go here and never inside Stamp, which stays ten
// multiplications and nothing else. A toll inside Stamp is a toll
// charged to every arena card, every identity herd and every bench
// that ever stamped a row, and those are numbers three volumes of this
// book have printed.
func Price(g *Genome, s Sense, t Temper) beast.Kind {
k := g.Stamp(beast.Fauna[g.Row])
k.Basal *= s.toll(g.Wire)
k.Basal *= t.watch(g)
return k
}
// watch is what wariness does to the Basal factor: one multiplier on
// the energy a gram of body costs every tick of a life.
func (t Temper) watch(g *Genome) float64 { return 1 + t.Watch*g.Temper[Wariness] }
// internal/gene/birth.go — the pool writes the blow's two numbers onto
// a body in the two places a creature comes into this world, and in no
// other place at all.
func (p *Pool) Stand(b *beast.Beast, g *Genome) {
p.Temper.Fit(b, g)
// ...
}
// ... and in Breed, on the tick a child is set down
b.Store -= e.Price
c := row.Spawn(at, v.Now)
c.Store = row.Full * Opening
p.Temper.Fit(c, kid)
$ go run ./cmd/temper -mode toll
temper: what wariness costs a standing body, against what an ear costs one
a browser is 40 grams at 0.004000 energy units a gram a tick, so a body
doing nothing whatever charges itself 0.160000 a tick and 576.00 a year
a controller wired to all seven chargeable ground channels pays 0.0350
of its Basal, and Watch is 0.07
wariness Basal a tick a full store a year vs seven
0.00 0.004000 0.160000 2500 ticks 576.00 0.00
0.25 0.004070 0.162800 2458 ticks 586.08 0.50
0.50 0.004140 0.165600 2416 ticks 596.16 1.00
0.75 0.004210 0.168400 2376 ticks 606.24 1.50
1.00 0.004280 0.171200 2337 ticks 616.32 2.00
the last column is the surcharge as a multiple of the whole sensory
suite, and the middle row of the table is where every founder stands
Price takes one more argument than it did and does one more multiplication,
and that is the whole of the standing charge. The argument is a rule and not a number:
it says what this particular valley charges, and a valley that leaves it alone charges
nothing, because the three fields of an empty rule are all nought and a share of nought
is nought. Founders and children go down that road together, so an animal the run was
founded with at wariness 0.50 pays exactly the surcharge a creature born in year nine at
wariness 0.50 pays. A valley that priced its first generation by one arithmetic and
every later generation by another would be measuring two animals and calling them one.
The last column of the table is where the constant was chosen. A founding animal sits at 0.50, in the middle of the gene's range, and its surcharge comes to 1.00 of the whole sensory suite: the wariness every creature in this valley is founded with costs it precisely what listening to all seven of the ground's channels would cost it. At the top of the range it is twice that. It is a real bill and a small one, and both halves of that sentence matter later on.
$ go test ./internal/beast/ -run 'AggressionRaisesBothEnds|WarinessDividesTheBlow|NoTemperamentTakesTheRows' -v
=== RUN TestAggressionRaisesBothEndsOfOneStrike
--- PASS: TestAggressionRaisesBothEndsOfOneStrike (0.04s)
=== RUN TestWarinessDividesTheBlowAndNotThePrice
--- PASS: TestWarinessDividesTheBlowAndNotThePrice (0.04s)
=== RUN TestABodyWithNoTemperamentTakesTheRowsOwnBlow
--- PASS: TestABodyWithNoTemperamentTakesTheRowsOwnBlow (0.01s)
PASS
ok theworld/internal/beast 0.087s
$ go test ./internal/gene/ -run 'StampStaysTen|FoundingWarinessCosts|OneRoadPricesFounder|TamenessIsReadByNothing' -v
=== RUN TestStampStaysTenMultiplications
--- PASS: TestStampStaysTenMultiplications (0.00s)
=== RUN TestAFoundingWarinessCostsWhatSevenChannelsCost
--- PASS: TestAFoundingWarinessCostsWhatSevenChannelsCost (0.00s)
=== RUN TestOneRoadPricesFounderAndChildAlike
--- PASS: TestOneRoadPricesFounderAndChildAlike (0.00s)
=== RUN TestTamenessIsReadByNothing
--- PASS: TestTamenessIsReadByNothing (0.00s)
PASS
ok theworld/internal/gene 0.004s
The first three walk a grid of both genes and check one quantity at a time: that the striker's share moves the price and the blow by the same number, that the struck body's share moves the blow and leaves the price where it was, and that a creature stood up by a bench which never handed it a genome throws and takes the row's own multiplication. The fourth of the second set is the odd one. It builds two genomes alike in everything except tameness, prices both, asks both for the two blow numbers, and prices a child of each, and it requires every one of those answers to be identical. It is a test that nothing happens, which is the only kind of test a gene nothing reads can be given.
Numbers first, on one hunter and one browser standing on touching cells. The hunter's row prices a strike as its whole body thrown at its top speed, which comes to 7.29 energy units. The browser's store holds 400.
The weapon. Aggression multiplies the throw. At the top of the range the throw is doubled, so it costs 14.58 and lands 14.58, and 400 divided by 14.58 is 27.4, so twenty-eight blows empty the store. Twenty-eight blows at 14.58 is 408.24. At the bottom of the range it is fifty-five blows at 7.29, which is 400.95. The two totals differ by seven units in four hundred. What changed is the calendar: a strike holds the body for the tick after it, so fifty-five blows take 109 ticks and twenty-eight take 55. Aggression buys ticks.
The armour. Wariness multiplies what arrives and leaves what is paid alone. At the top of the range half of every blow lands, so it takes a hundred and ten blows instead of fifty-five, and the striker pays 801.90 for a store holding 400. The extra 400.95 is burned in the striker's legs and arrives nowhere at all. Armour in this valley does not save its owner energy. It doubles the other animal's bill.
What the armour costs to wear. A browser is 40 grams and a gram costs 0.004 energy units a tick, so a body doing nothing charges itself 0.16 a tick and, over a year of 3,600 ticks, 576. The surcharge at the top of the range is seven hundredths of that: 40.32 energy units a year, which at four units a gram is 10.08 grams of tissue. At the founding 0.50 it is half that, 20.16 units and 5.04 grams, which is the same bill to the hundredth that a controller wired to all seven of the ground's channels pays.
And the law they are being measured against. One copy of a gene is
replaced outright once in a hundred and nudged eight times in a hundred, and those two
rates give a spread of s = 0.0300 of the gene's own range per copy. Copies
are independent, mean squares of independent moves add, so after g generations
the spread is s × √g. That law was derived from the two
mutation rates and never fitted to a run, and it is what a gene does when nothing
whatever is pushing it.
price = Lunge × (1 + Fury × a) and
blow = price × (1 − Ward × w)
blows = Store ÷ blow, rounded up, and
ticks = 2 × blows − 1
what a kill costs = blows × price ≈ Store ÷ (1 − Ward × w)
bill a tick = Bulk × Basal × (1 + Watch × w)
Three constants, three arguments. Fury is one because doubling is the largest factor that keeps the arithmetic something a reader can do without a calculator, and because multiplying both ends by the same number leaves the strike chapter's identity standing. Watch is seven hundredths because that puts a founding animal at exactly the cost of the only other standing charge this world has. Ward is a half for a reason that comes straight out of the drift law: a gene copied enough times is a number thrown evenly over its own range, and the law reaches that ceiling around the ninety-third generation. A Ward of 1.00 would mean that after ninety generations with no pressure on the value, some lineage somewhere is walking about unable to be hurt by anything in the world. A defect with no line of code to point at is the worst kind there is, and half is the cheapest way to make it impossible by arithmetic.
The Stamp multiplier boundary
There is one function in this valley that turns a genome into a row of prices, and it is
called Stamp. It takes a row out of the fauna, multiplies each of its ten
numbers by the factor standing over it, and hands the row back. Ten multiplications, no
conditions, no arguments beyond the row. A genome of all ones hands the row back
untouched, and untouched means bit for bit, because multiplying a float64 by exactly 1.0
is required to give that float64 back.
Wariness is a fact about a genome. Basal is one of the ten numbers.
Stamp is where a genome meets Basal. Every instinct says put the
surcharge there, and it would be one line, and it would be wrong in a way that no test in
this chapter would have caught.
Here is the mistake with the line put where it looks like it belongs. Stamp
is the place a genome's numbers reach a row, the surcharge is a genome's number reaching
a row, so the surcharge goes in Stamp:
func (g *Genome) Stamp(k beast.Kind) beast.Kind {
k.Bulk *= g.Body[Bulk]
// ... the other nine ...
k.Swing *= g.Body[Swing]
k.Basal *= 1 + Read.Watch*g.Temper[Wariness] // one line, and it looks right
return k
}
Everything on this page still passes. The blow tests never touch Stamp. The
pricing test asks for a surcharge and gets one. The valley runs, the ledger closes, the
populations look like populations. Then run the herd that this volume opened on: nineteen
creatures stamped out of a genome of all ones, scattered on the stream they have come off
since the creature chapters, each with a brain of its own, no pool anywhere and nothing
registered to breed them. The bench takes a flag so the two roads can be run against each
other instead of argued about.
$ go run ./cmd/temper -mode herd -inside
temper: 12x8 valley, tick 901, year 1 summer, 12 plants standing at 1517.5 grams
19 creatures founded on stream 12, each stamped from an identity
genome and given a brain of its own off stream 14
the ward's toll charged inside the stamp, so every one of them is
paying 3.5 percent on Basal for a wariness nothing here reads
this run before this reading holds
creatures founded 19 19 yes
creatures struck off 17 17 yes
still walking after 6750 ticks 2 2 yes
first death tick 1043 on 9,5 tick 1044 on 9,5 no
last death tick 3316 on 11,5 tick 3400 on 11,5 no
grams in 43193.623690 46567.944597 no
grams out 43193.623690 46567.944597 no
the two ledgers differ by -6.039e-10, which is the last bits of the adding
Three of the seven rows have moved. The first death arrives a tick early, the last eighty-four ticks early, and the grams the valley opened and closed on have gone from 46567.944597 to 43193.623690. The ledger still balances, which is the trap: an audit that adds up correctly says nothing at all about whether the numbers going into it are the numbers this book printed three volumes ago.
The reasoning from symptom back to cause is one step and it is a step about reach rather
than about arithmetic. Nothing in that herd reads a temperament gene. There is no pool, no
breeding pass, no valley rule saying that wariness costs anything. What those animals have
is a genome with 0.5 sitting in the wariness slot because that is the middle of the range,
and a single call to Stamp. Put the toll inside Stamp and every
one of them is charged three and a half percent more upkeep for a gene that decides
nothing in the run they are in. They starve sooner, they eat less, the plants they did not
eat go on growing, and 3,374 grams of the valley's history changes hands.
The general form applies outside this valley. A function that everything calls is the
most expensive place in a program to put a policy.
Stamp is called by the arena when it scores a genome, by every bench that
has ever stood a creature on a plate, and by the pricing road itself. Only the pricing
road knows which valley the animal is going to live in and what that valley charges for.
So the surcharge belongs one function further down, where the rule is in scope, and the
stamp stays ten multiplications with nothing in it that can be true in one run and false
in another. Take the flag away and the surcharge goes back to being a fact about a
valley these nineteen animals do not live in:
$ go run ./cmd/temper -mode herd | tail -9
creatures founded 19 19 yes
creatures struck off 17 17 yes
still walking after 6750 ticks 2 2 yes
first death tick 1044 on 9,5 tick 1044 on 9,5 yes
last death tick 3400 on 11,5 tick 3400 on 11,5 yes
grams in 46567.944597 46567.944597 yes
grams out 46567.944597 46567.944597 yes
the two ledgers differ by -1.455e-11, which is the last bits of the adding
The sixteen-year gene run
Now put both readers in a valley and let it go. Sixteen cells by twelve, browsers scattered on the stream they have come off since the creature chapters, hunters after them on the stream this volume opened, every animal driven by a wiring that grows its own nodes, births sorted into species, and every birth and every death written to an archive that nothing in the running world ever reads back.
$ go run ./cmd/temper -mode valley | tail -126 | head -17
temper: 16x12 valley, 21 browsers off stream 12 and 8 hunters off stream 22,
aggression on both ends of a strike, wariness on one end of it and
on every tick of a life: Fury 1.00, Ward 0.50, Watch 0.07
every birth and every death written to an archive of its own, which
nothing in the running valley ever reads back
year browsers hunters standing lying kills born
1 21 8 4654.10 0.95 0 0
2 7 0 6133.75 159.75 13 44
3 12 0 7449.85 157.02 13 70
5 38 0 7289.73 416.80 13 187
9 247 0 2973.75 1464.72 13 1144
13 263 0 332.60 149.77 13 1545
17 16 0 0.00 156.49 13 1727
29 founded, 1727 born, 1740 struck off, 16 still walking after 57150 ticks
the last hunter was struck off on tick 2739, 1838 ticks after the founding
Two things in that table should be named before anything is measured off the run. The hunters are gone by the end of the second year, thirteen kills between the eight of them, which is the row failing to make a living in the way the strike chapter measured it failing. And the browsers boom to two hundred and sixty-three by the thirteenth year, strip the standing plants to nothing, and crash to sixteen. Do not read either of those as something the two readers did. With the readers switched off, the same founding ends somewhere else entirely, and the two runs part company in the first year: by the ninth they are two different worlds, and comparing a population figure across them says nothing about a gene. That is the strike chapter's warning applying here word for word, and it is why every measurement below is taken across genes inside one run.
$ go run ./cmd/temper -mode valley | tail -108 | head -27
the two rows' accounts, added up over every animal that ever lived
browsers hunters
animals that ever lived 1740 16
creature-ticks they were stepped 8739107 7452
energy that came in 3376816.07 532.64
energy standing still charged 636486.44 1818.33
income as a share of rent 530.54% 29.29%
strikes they threw 0 79
energy those strikes cost to throw 0.00 992.81
energy blows took off them 742.22 0.00
their mean share added to a blow 0.5453 0.5018
their mean share turned aside 0.2502 0.2473
what the two readers did to the blows this run threw
energy the strikers swung at the row below 992.81
energy that arrived, at the wariness they had 742.22
energy that would arrive at wariness 1.00 496.40
so a full ward would have saved the row 496.40
what the standing charge cost the same row over the same run
energy standing still charged them 636486.44
the same bodies at wariness nought 615035.12
so a full watch would have charged them 43052.46
the charge, for every unit the ward saves 86.73
The last number of that block is the argument of the chapter arriving as a measurement. Over sixteen years the hunters swung 992.81 energy units at the browsers. The browsers' own wariness, sitting where a founding leaves it, turned aside a quarter of that and took 742.22. Push every browser in the valley to the top of the wariness range and the arriving total drops to 496.40, so the most a perfect ward could have saved the whole row over sixteen years is 496.40 energy units. At four units a gram that is a hundred and twenty-four grams of tissue, shared out among seventeen hundred animals.
The bill for that ward is on the second half of the block. Those same bodies charged themselves 636,486 energy units to stand about, and at a wariness of nothing they would have charged 615,035. Seven percent of the second figure is 43,052. So a row of browsers that all wore the best armour available would have paid 43,052 for something that saved them 496: eighty-seven units charged for every unit turned aside.
That ratio belongs to the valley and not to the gene. Thirteen browsers out of 1,740 died with a blow on them here, and the number would fall through the floor in a valley where the hunters could feed themselves. What it does say, before a single drift table is read, is which way the ledger is leaning: in this valley, on these numbers, an animal that is cheap to keep beats an animal that is hard to hurt, and it beats it by two orders of magnitude.
The drift-law comparison
The archive is the instrument for the rest of it, and it is read the way the volume that
wrote it reads it: open the file, turn every born record back into a genome, count each
creature's generations by walking the parent field up to a founder, pool the creatures into
bands so no row rests on a handful of animals, and never ask the valley anything. The law
is s × √g with s = 0.0300, worked out from the two
mutation rates and not from this or any other run.
$ go run ./cmd/temper -mode valley | tail -75 | head -40
how far apart the genes are, as a share of each gene's own range
gen of them aggression wariness tameness body s x sqrt(gen)
0 29 0.0000 0.0000 0.0000 0.0000 0.0000
1-2 43 0.0059 0.0119 0.0095 0.0242 0.0336
3-4 43 0.0964 0.0055 0.0201 0.0278 0.0570
5-6 74 0.1401 0.0091 0.0547 0.0308 0.0702
7-8 63 0.1779 0.0167 0.0830 0.0527 0.0823
9-10 78 0.2169 0.0314 0.1193 0.0631 0.0928
11-12 92 0.2158 0.0647 0.1294 0.0668 0.1019
13-14 109 0.2757 0.1146 0.1846 0.0787 0.1102
15-16 144 0.2336 0.1324 0.2070 0.0655 0.1184
17-18 203 0.1461 0.1695 0.2328 0.0599 0.1257
19-20 242 0.1347 0.1615 0.2394 0.0617 0.1325
21-22 254 0.1545 0.1737 0.2144 0.0553 0.1389
23-24 214 0.1449 0.1823 0.2203 0.0595 0.1452
25-26 104 0.1206 0.2034 0.1756 0.0511 0.1514
27-28 60 0.1057 0.2179 0.2136 0.0559 0.1567
29-30 4 0.0174 0.0524 0.2162 0.0871 0.1616
the same rows as shares of the law, so 1.000 is a gene wandering
exactly as fast as one nothing is pushing
gen of them aggression wariness tameness body
1-2 43 0.175 0.355 0.284 0.720
3-4 43 1.692 0.097 0.353 0.489
5-6 74 1.997 0.129 0.779 0.438
7-8 63 2.162 0.203 1.009 0.640
9-10 78 2.338 0.338 1.286 0.680
11-12 92 2.119 0.635 1.270 0.656
13-14 109 2.501 1.039 1.674 0.714
15-16 144 1.974 1.119 1.749 0.553
17-18 203 1.163 1.349 1.853 0.476
19-20 242 1.016 1.219 1.807 0.466
21-22 254 1.112 1.250 1.543 0.398
23-24 214 0.998 1.256 1.518 0.410
25-26 104 0.797 1.344 1.160 0.338
27-28 60 0.674 1.390 1.363 0.357
29-30 4 0.108 0.324 1.339 0.539
mean 1.388 0.803 1.266 0.525
Read the body column first, because it is the one this book already had an answer for. The ten factors the ledger prices on every tick of every life sit at about half of what a gene does when nothing is pushing it, and they sit there for the whole run: 0.720 in the shallowest band and 0.357 in the deepest, averaging 0.525. That is selection with its hands on something. Ten numbers that decide whether an animal can afford a child are held inside half the room a free number would have taken.
Now the three temperament columns, and they do not separate. The mean row reads 1.388, 0.803 and 1.266. Wariness is the lowest of the three, which is the direction the ledger ought to be pushing it, but aggression is the highest and aggression is read by nothing at all in this run: a browser cannot take the seventh entry, so its aggression multiplies nothing for the whole of its life. Two genes that no line of code looks at have come out on either side of the one that is being charged for. Individual bands are worse: the early wariness rows sit at a tenth of the law, which looks like enormous selection and is a bottleneck, because the population fell to seven browsers in the second year and everything after generation three descends from those seven.
$ go run ./cmd/temper -mode valley | tail -34 | head -19
and where the middle of each gene has moved to, from a founding 0.5000
gen of them aggression wariness tameness
0 29 0.5000 0.5000 0.5000
1-2 43 0.5007 0.4991 0.4986
3-4 43 0.4937 0.5002 0.5018
5-6 74 0.5089 0.5025 0.5091
7-8 63 0.5701 0.5053 0.5290
9-10 78 0.6500 0.5066 0.5493
11-12 92 0.6719 0.5009 0.5618
13-14 109 0.6032 0.5222 0.5999
15-16 144 0.6144 0.5303 0.6604
17-18 203 0.5166 0.5103 0.6934
19-20 242 0.5248 0.5090 0.6429
21-22 254 0.5286 0.4682 0.6536
23-24 214 0.5165 0.4766 0.6203
25-26 104 0.5040 0.5183 0.6140
27-28 60 0.4701 0.4976 0.5811
29-30 4 0.4873 0.4727 0.5763
The middles say the same thing more plainly. Thirty generations in, wariness has moved from 0.5000 to 0.4727, about half of one of the mutation pass's nudges, while tameness has gone the other way and gone nearly three times as far. Sixteen years of this valley cannot tell you which of the three genes has a bill on it.
The reason is not subtle and it was in the arithmetic before the run: every founder in that valley opens at 0.5000, so at tick one there is nothing for a ledger to prefer. Selection works on differences, mutation makes differences at 0.0300 of a range per generation, and a difference in wariness of three hundredths is a difference in upkeep of two tenths of one percent. The valley spent its thirty generations manufacturing the variation instead of sorting it.
The same valley, founded across the range
So hand the ledger the variation instead of making it wait. Found the population with the three temperament genes spread evenly from one end of their range to the other, the same set of numbers for all three, dealt out in three different orders so that no two of them start out related. Nothing is drawn: each founder's genes come off its own index in the founding, so the run spends exactly the numbers a plain founding spends and every stream stands where it stood.
Then run it twice. Once with the readers on, and once with the rule left empty, which is a valley where all three genes are ornaments and the three columns are three samples of pure drift through one history.
$ go run ./cmd/temper -mode valley -spread -blind | tail -98 | head -17
temper: 16x12 valley, 21 browsers off stream 12 and 8 hunters off stream 22,
and nothing in it reading a temperament gene at all
the founders' three temperament genes spread evenly over the range,
one set of numbers dealt out in three different orders
every birth and every death written to an archive of its own, which
nothing in the running valley ever reads back
year browsers hunters standing lying kills born
1 21 8 4654.10 0.95 0 0
2 11 0 6668.07 144.04 13 46
3 24 0 6999.00 137.63 13 78
5 19 0 7254.60 513.50 15 228
9 79 0 6464.87 1091.71 15 879
13 223 0 4684.13 643.67 15 1606
17 257 0 3673.12 151.79 15 1816
29 founded, 1816 born, 1588 struck off, 257 still walking after 57150 ticks
That table is worth a moment on its own, because it says two things at once. Twenty-one browsers were founded with wariness running from one end of the range to the other, and the valley they made is identical, year for year and kill for kill, to the valley this volume ran before any of this existed. Spreading three genes that nothing reads across a whole population changes not one tick. That is the empty rule being empty by arithmetic rather than by a promise, and it is also the check that the founding spends no numbers: a founding that had drawn its genes would have moved every stream behind it.
$ go run ./cmd/temper -mode valley -spread -blind | tail -29 | head -21
and where the middle of each gene has moved to, from a founding 0.5000
gen of them aggression wariness tameness
0 29 0.5000 0.5000 0.5000
1-4 93 0.6205 0.7467 0.6834
5-8 120 0.8027 0.9740 0.8523
9-12 153 0.8367 0.9798 0.7901
13-16 272 0.8524 0.9827 0.7981
17-20 411 0.8439 0.9757 0.8320
21-24 363 0.8469 0.9587 0.8930
25-28 227 0.8571 0.9665 0.9069
29-32 130 0.8433 0.9699 0.9069
33-36 36 0.8131 0.9624 0.9258
37-40 11 0.7575 0.7860 0.9418
the three genes over the 257 creatures still walking at the end
gene founding mean sd sd / law
aggression 0.5000 0.8477 0.0705 0.475
wariness 0.5000 0.9558 0.1094 0.736
tameness 0.5000 0.9298 0.0777 0.523
All three climb hard out of the founding and then stop. That is not selection: it is twenty-one founders being cut to a handful in the second year and the survivors happening to be carrying high numbers, after which each of the three sits where the bottleneck left it for thirty generations. Aggression settles around 0.84, wariness around 0.97, tameness around 0.90, and the whole point of this run is that all three of them are doing the same thing, because the same thing is being done to all three of them.
$ go run ./cmd/temper -mode valley -spread | tail -30 | head -22
and where the middle of each gene has moved to, from a founding 0.5000
gen of them aggression wariness tameness
0 29 0.5000 0.5000 0.5000
1-4 61 0.5568 0.6704 0.5811
5-8 62 0.7771 0.9132 0.7798
9-12 84 0.7871 0.9321 0.9197
13-16 234 0.7944 0.9039 0.9483
17-20 286 0.8062 0.8000 0.9320
21-24 412 0.8002 0.7668 0.9449
25-28 386 0.8043 0.7597 0.9146
29-32 247 0.7787 0.7759 0.9456
33-36 139 0.8001 0.8048 0.9688
37-40 80 0.7962 0.6231 0.9604
41-44 20 0.8058 0.6347 0.8973
the three genes over the 8 creatures still walking at the end
gene founding mean sd sd / law
aggression 0.5000 0.8153 0.0138 0.088
wariness 0.5000 0.5696 0.3862 2.472
tameness 0.5000 0.9367 0.1613 1.032
Aggression settles at 0.80 and tameness at 0.94, which are the numbers the blind run settled on to within about five hundredths. The two genes nothing reads land in the same place whether or not the valley is reading anything, which is what two controls are for. Wariness does not. It climbs out of the bottleneck like the other two, to 0.9321 by the twelfth generation against the blind run's 0.9798, and then it comes back: 0.80, 0.77, 0.76, 0.78, 0.80 across the generations where the bands are largest, against a blind run sitting at 0.97 and 0.96 over exactly those generations. Two tenths of the gene's range, in the direction the eighty-seven-to-one arithmetic said it should go, in the one column of the three that has a bill attached to it.
The last two bands go further, to 0.6231 and 0.6347, and they should be read with more care than the rest: those creatures were born into a valley whose plants were already gone, and eighty and twenty animals is not many. The claim this run supports is the plateau and not the tail. Wariness settled about two tenths lower than two genes that were treated identically in every other respect, and it is the only one of the three that the valley was charging for.
The three valleys on this page end on lines the blocks above leave out: 57,150 ticks in
23.777 seconds for the first, 26.024 for the founding spread across the range and 25.776
for its blind twin, which is between 2,100 and 2,400 ticks a second on an eight-core
Ryzen 7 3700X and different on your machine. The tick counts are arithmetic and are
compared like every other number here. The durations are this desk talking. Each of the
three writes an archive of about sixteen megabytes as it goes and takes it away again
once it has read it; -log gives the file a name and leaves it there.
Three short valleys is more than a chapter of this volume is allotted, and the reason
they are here is that the pair is the measurement: a founding with the genes spread and
the same founding with the readers switched off are one instrument and neither half of
it means anything alone. The first, the null at 0.5000, is what makes the pair worth
running rather than a result taken on faith. Between them they come to seventy-six
seconds, which is less than a quarter of what a single long valley costs.
Why holding costs leave a reading
Take the animals out and there is a distinction here that turns up wherever anything is paid for. A cost is charged either when a capability is used or for as long as it is held, and the two behave nothing like each other under selection, under budgeting, or under any other process that keeps what pays for itself.
A use cost is invisible until it is used, and then it is large. Aggression multiplies a strike by up to two. On the seventy-nine ticks of this run where a strike was thrown that is an enormous number, easily the largest single factor anywhere in a creature's tick. On the other eight and three-quarter million creature-ticks of the same run it is worth precisely nothing, and on every tick of every browser's life it is worth nothing for ever, because a browser has nothing beneath it to strike. A gene like that is invisible to the ledger in any animal that does not use it, which means it can drift as freely as a gene with no reader. It has a reader. It has no readings.
A standing cost is charged always, and is therefore small. Nobody would
write a surcharge on Basal at fifty percent, because it would be the loudest
number in the tick and would swamp the ten factors it sits beside. So it is set at
something modest, and something modest multiplied by every tick of every life is a large
total spread so thinly that no single generation can feel it. The run measured that
exactly: 43,052 energy units charged across a row over sixteen years, which is a real
quantity, arriving as three and a half percent on each animal, which is inside the noise
of who happened to be standing next to a plant in a good week.
Put the two together and you get the rule this page was built to test. The cost that is easiest to measure is the hardest to select on, and the cost that dominates a moment is the one that never gets a chance to be charged. Aggression's reader is the strongest in the module and the row that carries it dies out in the second year. Wariness's reader is charged eight and a half million times and is worth two tenths of a gene after thirty generations, and it took a founding designed for the purpose to see even that.
There is a lesson about instruments in here as well, and it is the more useful half. The first run of this valley founded every animal at the middle of the range and came back with three columns that could not be told apart, and it would have been easy to publish that as "the reader does nothing". It does not say that. It says a population with no variation in a gene gives selection nothing to work with, and thirty generations of mutation at three hundredths a copy is not enough to make some. Change the founding, keep two genes as controls inside the same run, and the measurement appears. When a measurement comes out flat, the first question is whether the thing being measured was ever present in the sample.
The third gene has been sitting in the genome through all of this doing exactly what it did in the volume that installed it, which is nothing. It costs a couple of dozen bytes on every born record in the archive, and it earned every one of them on this page: tameness is the column both of the other two were held against. A control group is not a thing you can add to an experiment afterwards, any more than a column can be added to a record of animals that are already dead.
- Given a hunter at aggression 0.75 and a browser at wariness 0.25 holding 300 energy units, work out what the hunter pays a blow, what arrives, and how many ticks of standing next to it the kill takes.
- Say why doubling a striker's aggression leaves the total cost of emptying a store almost unchanged, and why doubling a target's wariness does not.
- Name the function the two tolls are applied in, the function they must stay out of, and the run that goes wrong first when they are put in the wrong one.
- Explain what the body column of the drift table is doing at 0.525 while the three temperament columns sit near 1.000, without using the word noise.
- Given the run's figure of eighty-seven units charged for every unit turned aside, say what would have to change about the valley to bring it near one.
- Say why the first valley on this page could not have measured selection on wariness whatever its result had been, and what the second one changed about the founding to make the question answerable.
Exercise 1 — both genes at the top. A hunter at aggression 1.00 swings at a browser at wariness 1.00 holding 200 energy units. Work out the price, the blow, the number of blows and the total spent, then check the ends of it against the bench.
The price doubles to 14.58 and then half of it is turned aside, so 7.29 arrives, which is the row's own untouched blow. Two hundred divided by 7.29 is 27.4, so 28 blows, and 28 at 14.58 is 408.24. The two genes at their extremes cancel in what lands and do not cancel at all in what is paid: the hunter spends twice what an animal at the bottom of the aggression range would spend to do the identical damage, and gets the kill in 55 ticks instead of 109.
The bench takes the store as a flag, and the bottom row of its second table is the half of this you can check directly:
$ go run ./cmd/temper -mode blow -store 200 | tail -10
wariness paid landed blows ticks spent
0.00 7.2900 7.2900 28 55 204.1200
0.25 7.2900 6.3788 32 63 233.2800
0.50 7.2900 5.4675 37 73 269.7300
0.75 7.2900 4.5563 44 87 320.7600
1.00 7.2900 3.6450 55 109 400.9500
here the paid column stands still and the last one climbs. The share
comes off what arrives and off nothing the striker is charged, so a
wary animal makes emptying its store of 200 cost more than 200
A store of 200 against a wariness of 1.00 costs 400.95, which is the same 55 blows at 7.29 that a store of 400 costs against a wariness of nothing. Halve what lands and you have doubled the price of everything the striker wanted.
Exercise 2 — give aggression a population. The gene with the strongest reader in the module is carried by a row that dies out in the second year. Found the valley with three times as many hunters and find out whether that is enough to get a measurement out of it.
It is not, and the run says why in two numbers:
$ go run ./cmd/temper -mode valley -hunters 24 -years 6 -every 2 | tail -130 | head -15
temper: 16x12 valley, 21 browsers off stream 12 and 22 hunters off stream 22,
aggression on both ends of a strike, wariness on one end of it and
on every tick of a life: Fury 1.00, Ward 0.50, Watch 0.07
every birth and every death written to an archive of its own, which
nothing in the running valley ever reads back
year browsers hunters standing lying kills born
1 21 22 4654.10 0.95 0 0
2 5 0 5925.66 196.03 21 56
3 9 0 7566.43 117.39 22 73
5 12 0 7332.46 250.01 22 125
7 20 0 7178.09 387.44 22 188
43 founded, 188 born, 211 struck off, 20 still walking after 21150 ticks
the last hunter was struck off on tick 2382, 1481 ticks after the founding
Twenty-two hunters were founded, they killed twenty-two browsers between them, and the last of them was struck off 1,481 ticks after the founding, which is about four tenths of a year. Three times the hunters bought that row less time on the ground, not more, and none of that is a fact you can hold against the eight-hunter valley above: two foundings are two worlds. What it settles inside its own run is that a gene needs generations before selection can do anything with it, and this row does not get a second one. The reader exists, its arithmetic is exact, and the population it applies to rounds to nothing beside the population of the valley. That is the honest state of aggression at the end of this page, and it is a fact about the second row and not about the gene.
Exercise 3 — test that nothing happens. Write the test that says a gene is read by nothing. It is harder than it sounds: the assertion has to cover every road a gene could take out of a genome, and there is no compiler error to tell you when you have missed one.
Two genomes alike in everything but the gene, and then every answer the module can give about a creature: the row it is priced to, the two numbers it carries into a blow, and what a child of it costs.
one := Root(mind.Brains(5), NewMint(Wide, beast.Acts), 0)
two := one.Copy()
one.Temper[Tameness], two.Temper[Tameness] = 0, 1
a, b := Price(one, Sense{On: true}, Read), Price(two, Sense{On: true}, Read)
if a != b {
t.Errorf("tameness priced two rows apart:\n%+v\n%+v", a, b)
}
if Read.Throw(one) != Read.Throw(two) || Read.Guard(one) != Read.Guard(two) {
t.Errorf("tameness reached a blow")
}
if Costs(a) != Costs(b) {
t.Errorf("tameness moved the price of a child")
}
$ go test ./internal/gene/ -run TestTamenessIsReadByNothing -v
=== RUN TestTamenessIsReadByNothing
--- PASS: TestTamenessIsReadByNothing (0.00s)
PASS
ok theworld/internal/gene 0.004s
The first comparison is the one doing the work, and it is doing it because
beast.Kind is a flat struct of numbers with nothing nested inside it, so
a != b compares all twelve of its fields at once and cannot be made to
pass by a reader added to a field the test forgot to name. Write the assertion
against the whole thing wherever the language lets you. A test that lists the fields
it cares about is a test that will keep passing on the day somebody adds a
thirteenth.
Two of the three genes have readers, one of them has a population to be read in, and the valley they live in has eaten itself down to sixteen animals and no standing plants at all. Nothing in this world caps a population, nothing counts the browsers and decides there are too many, and the only thing standing between a row of eaters and an empty valley is how fast the plants can grow and how cold the winter gets.