The World Vol 6 · Evolution
ch 68 / 105
Chapter 68

A Lineage Looks Like Itself

Look genes in the genome

The genome's fourth block has four numbers that the valley has carried and never opened. Ten body factors are priced every tick, the controller block drives action, and three temper genes still wait for behavior to read them. Appearance is computed from the genome every time a creature is drawn and is never stored on the creature.

Nothing on beast.Beast names a colour, a size or a row of a sheet. The drawing substitutes into an indexed palette: a base sprite is drawn in eight of the valley's sixteen colours, and a creature is turned into one of four ramps, two marking strengths, a cover choice and a body scale.

Storage would make appearance another state to keep in sync with inheritance. If a genome changes and a stored coat does not, the archive and the client disagree about what lineage looks like.

Computation keeps the visual trait tied to the same file that records ancestry. A child that inherits a look gene also inherits the look derived from it, and an archive reader can draw the same animal without a saved sprite.

The page opens the sprite sheet, derives a table of sixteen numbers, checks how body mass changes drawn width, and prints four species that share an ancestor. Their differences are phenotype, not hand-assigned paint.

◆ Note — bench lineages

The animals at the end of this chapter are made on a bench and not in The Hollow. A bench here is one founding genome copied, mutated and grown for thousands of births in a row, with no ground under it, no store, no mate and no death: the same three operators the valley uses, run for far longer than any valley in this book has time for. It is the only place this volume can get four lineages far enough apart to be four species, because the two centuries already put through this valley produced one species and stayed there.

Everything on this page that is a fact about a genome is as true on the bench as in a valley: how far two of them are apart, what a look gene says, what the archive kept and what a sprite comes out as. Nothing here may be set beside a population figure or a digest from a valley run.

The eight-colour sprite sheet

The palette came first, several volumes ago, and it has not moved since: sixteen colours chosen in advance, written to a file, with nothing outside them allowed anywhere on any asset. One of the sixteen is ink, the dark anchor every outline is drawn in. One is bone, the light anchor. The twelve in between are four ramps of three — soil, rock, water and growing green — each holding a shadow, a midtone and a lit tone of one material. The last two are a warm accent pair kept for things that need to catch the eye, and no creature uses them.

A coat has to be a whole ramp and not a single colour. A body drawn in one flat colour has no form in it; a body drawn in three has a lit top, a midtone flank and a shaded belly, and the three of them have to move together. Swap one entry of a three-entry ramp for one entry of another and the animal comes out half brown and half green. Swap all three and it comes out green, shaded exactly the way the brown one was. That is the whole argument for making the coat gene pick a ramp: four of them, one per material, and a gene between 0.00 and 1.00 lands in one of four bands of equal width.

Four coats is not many, and four coats is what sixteen colours buy. The consequence arrives at the end of this page: two animals of two different species can share a coat, and what separates them on the screen turns out to be their markings, their parts and their size.

▣ Build · stage 1 — a creature sheet, drawn as characters

The sheet is authored in code. Every pixel is a letter, every letter is a palette entry, and a space is a pixel that is not there, so the transparent parts of the listing are transparent in the file too.

// cmd/mkbeasts/art.go
//
//	o  outline       ink, never remapped
//	d  coat shadow   the belly and the inside of the legs
//	b  coat midtone  most of the body
//	l  coat lit      the head, and whatever a variant raises
//	m  muzzle        the smallest marking patch, 2 pixels
//	f  flank         the middle one, 3 pixels
//	k  back band     the largest, 5 pixels
//	e  eye           bone, never remapped

// plain is the founding animal: a low-backed browser with its head
// above its back, a short tail and two planted feet. All variants keep
// the same three marking patches and the same foot anchor at row 13.
var plain = []string{
	"                ",
	"                ",
	"                ",
	"                ",
	"          o     ",
	"          oooo  ",
	"   olllllloleoo ",
	"ooobkkkkkbolbbo ",
	" oobfffbbboommo ",
	" oobddbbbboooo  ",
	"  ooddddddoo    ",
	"   odooooodo    ",
	"    do    do    ",
	"   ooo    ooo   ",
	"                ",
	"                ",
}

Painting one of those into the sheet is a lookup and a Set. The lookup is the part that matters: a character with no palette entry behind it stops the program instead of putting a colour nobody chose into an asset.

// cmd/mkbeasts/main.go
for y, line := range mask {
	for x := 0; x < gene.Cell; x++ {
		ch := line[x]
		if ch == ' ' {
			continue
		}
		i, ok := at(ch)
		if !ok {
			die(fmt.Errorf("row %d line %d: no palette entry for %q", row, y, ch))
		}
		sh.Set(col*gene.Cell+x, row*gene.Cell+y, gene.Sixteen[i])
	}
}
$ go run ./cmd/mkbeasts -to .
mkbeasts: beasts.png, 32x64, 2 poses across and 4 parts down at 16 pixels, 483 bytes
  pixels sha256 ff0c918997ddf0280e86e27dff7ac4951a8eb434177dc593ae071fe856415ec0

  the eight entries the sheet is drawn in, by their index in the valley's sixteen
    o  entry  0  181A29  outline
    d  entry  1  4C2F22  coat shadow
    b  entry  2  75563B  coat midtone
    l  entry  3  A1875A  coat lit
    m  entry 10  254C39  muzzle
    f  entry 11  4C7841  flank
    k  entry 12  90A858  back band
    e  entry 15  F5EBD7  eye

  part          drawn  outline   coat muzzle  flank   band
    plain          88       44     33      2      3      5
    crest         100       51     38      2      3      5
    horns          97       51     35      2      3      5
    longneck       95       52     32      2      3      5

mkbeasts: beast-8.png, 8x1, 8 entries, 94 bytes
  pixels sha256 1523dd28f05d9b50e6298244bf8e4796907588c0dff26f2c8b2901343b4a6877
  pixels on the sheet the strip does not hold: 0
  beasts.png is byte for byte assets/sprites/beasts.png
  beast-8.png is byte for byte assets/palette/beast-8.png

Two files come out. The sheet is thirty-two pixels by sixty-four: two poses across and four part variants down, each cell sixteen on a side. The strip is eight pixels by one, one pixel per entry the sheet is drawn in, which is the same form the valley palette has been shipped in since it was chosen. Run with -to naming a directory, the generator writes the pair somewhere else and compares them with the pair the book ships, which is how a picture stays checkable: nobody has to trust that the committed file is what the code makes, because the code says so every time it runs.

Read the sheet's own palette down the middle column of that run and the design is visible. The three coat entries are the soil ramp, shadow, midtone and lit. The three marking entries are the green ramp, in the same order of brightness. Ink and bone stay where they are. The base sheet is therefore a legal picture of this valley in its own right — a brown animal with a green band down its back — and it is also a template, in which the browns mean coat and the greens mean marking.

The four part variants are the piece of this that had to be drawn instead of derived. A variant is a row of the sheet, and the fourth look gene picks the row. Row 0 is the plain animal: low back, head above it, short tail and planted feet. Row 1 raises a four-pixel sail over the middle of the back, lit and not banded, because it is the highest thing on the animal and the light in this valley comes from above. Row 2 puts a pair of horns on the crown, each tip stepping outward from its stem so that two of them read as two. Row 3 lifts the head three rows and joins it to the shoulder with a narrow neck. The muzzle moves with the head; the flank and back patches stay in place.

All four differences are above the line of the animal's back, and that is a consequence of the budget and not a preference. Sixteen pixels holds a body, a head and two visible legs, with a short tail reaching the left edge. At small sizes that tail can disappear between samples; the raised parts have more room to remain distinct. Both poses keep their feet on row 13, so the existing foot anchor still plants the animal on the ground.

▧ Tool — drawing the sheet by hand instead

Nothing here needs a pixel editor, and everything here can be done in one. LibreSprite (libresprite.github.io) will import assets/palette/valley-16.hex as a palette, which reduces its colour picker to exactly the sixteen entries this world allows, and its one-pixel pencil then cannot put anything else on the canvas. Draw a thirty-two by sixty-four sheet in it, export with a real alpha channel, and everything below reads it the same way. The eight-pixel strip is the shorter list to load when you only want the entries a creature uses.

The look table

The derivation is small enough to read in one go. Four genes come in between 0.00 and 1.00, one body factor comes in as a multiplier, and a Look comes out: which ramp the coat is, which ramp the markings are, how many of the three marking patches carry them, which row of the sheet the animal was born with, and how many pixels across it is drawn. There is no state in it and nothing is written back anywhere.

▣ Build · stage 2 — five numbers out of five numbers
// internal/gene/look.go
type Look struct {
	Coat  int // which ramp the coat is, 0 to Hues-1
	Mark  int // which ramp the markings are
	Marks int // how many of the sheet's three marking patches carry it
	Part  int // which row of the sheet the creature was born with
	Size  int // how many pixels across it is drawn
}

// Shown is the whole derivation: four look genes and one body factor
// in, one Look out, no state anywhere.
func Shown(g *Genome) Look {
	return Look{
		Coat:  band(g.Look[Coat], Hues),
		Mark:  band(g.Look[Mark], Hues),
		Marks: band(g.Look[Cover], Marks+1),
		Part:  band(g.Look[Part], Parts),
		Size:  Pixels(g.Body[Bulk]),
	}
}

// band puts a gene in 0..1 into one of n slots of equal width. The top
// of the range belongs to the last slot rather than to a slot of its
// own, which is the one place a gene of exactly 1.00 would otherwise
// fall off the end.
func band(x float64, n int) int {
	i := int(x * float64(n))
	if i >= n {
		i = n - 1
	}
	if i < 0 {
		i = 0
	}
	return i
}

band is three lines of arithmetic and two guards, and the first guard is the whole point. A look gene is clamped to 0.00 through 1.00 inclusive, so a mutation pass that lands on the top of its range hands over exactly 1.00, and 1.00 times four is four: a fifth band in a table of four. Exactly 1.00 is also the value this arithmetic meets most often, because every gene the mutation pass would have carried past the top of the range stops on it instead.

The coverage gene reads a little differently from the two hues, and the difference is deliberate. A hue picks one of four ramps, so four bands. Coverage picks how many of the three marking patches are remapped, and none is an answer: no markings at all is a perfectly good animal. So coverage lands in one of four bands too, numbered zero to three, and the three patches are taken in a fixed order, smallest first. The muzzle is two pixels of the sprite; the flank patch adds three, and the band down the back adds five. A creature at coverage 1 carries two marked pixels of the eighty-eight a plain animal is drawn from. Coverage 2 marks five pixels in total; coverage 3 marks ten.

▣ Build · stage 3 — the substitution, sixteen entries wide

The table is the whole mechanism. Entry i of the palette comes out as entry Table()[i], and everything the table does not name comes out as itself.

func (l Look) Table() [Colours]int {
	var t [Colours]int
	for i := range t {
		t[i] = i
	}
	coat, mark := Ramps[l.Coat], Ramps[l.Mark]
	for k := range Base {
		t[Base[k]] = coat[k]
	}
	for k := range Marked {
		if k < l.Marks {
			t[Marked[k]] = mark[k]
		} else {
			t[Marked[k]] = coat[k]
		}
	}
	return t
}

Three rules. The sheet's coat entries come out as the coat ramp. Each marking entry comes out as the marking ramp's entry of the same brightness when the coverage reaches it, and as the coat's entry of that brightness when it does not. Everything else, ink and bone included, is a fixed point. That second rule is the one worth sitting with: an unmarked patch is not drawn and then covered up, it is drawn in the coat's own tone of the right brightness, so it stops existing without leaving a seam where it used to be.

$ go run ./cmd/looks -mode palette | head -13
looks: the palette a creature is drawn out of

  assets/palette/valley-16.hex holds 16 colours; the package holds 16
  entries that disagree between the file and the code: 0

   ramp   shadow  midtone      lit
   soil        1        2        3   4C2F22 75563B A1875A
   rock        4        5        6   323847 5A6470 8A979E
  water        7        8        9   243E6B 3D7799 6DBEC7
  green       10       11       12   254C39 4C7841 90A858

  assets/palette/beast-8.png is 8 entries wide, and every one of them is in the sixteen: yes
  assets/sprites/beasts.png is 32x64, 2 poses across and 4 parts down

The first thing that run does is read the palette file off disk and compare it with the sixteen colours written down in the package. A palette written down twice can disagree with itself, and the day it does, every asset in the tree is still legal and every creature drawn out of the code is not.

Now the blit. The existing one copies a rectangle of a sheet onto the framebuffer at its own size, mixing each pixel by its own alpha, and it has been doing that since the sprite chapter without changing. What this chapter needs is a second entry point beside it: one that samples a rectangle into a rectangle of a different size, and puts every pixel through a substitution on the way.

▣ Build · stage 4 — one blit that samples and substitutes
// internal/render/remap.go
type Remap struct {
	From []Color // the palette the sheet was drawn in
	To   []Color // what each of those entries comes out as
}

func (m *Remap) Of(c Color) Color {
	if m == nil {
		return c
	}
	for i, was := range m.From {
		if was == c && i < len(m.To) {
			return m.To[i]
		}
	}
	return c
}

func (b *Buffer) BlitMap(s *Sheet, src Rect, dx, dy, dw, dh int, m *Remap) {
	sw, sh := src.X1-src.X0, src.Y1-src.Y0
	dst := Rect{dx, dy, dx + dw, dy + dh}.Intersect(b.clip)
	for y := dst.Y0; y < dst.Y1; y++ {
		sy := src.Y0 + (y-dy)*sh/dh
		for x := dst.X0; x < dst.X1; x++ {
			sx := src.X0 + (x-dx)*sw/dw
			c := m.Of(s.Pix.Pix[s.Pix.index(sx, sy)])
			switch c.A() {
			case 0: // not there: leave the framebuffer alone
			case 255: // solid: overwrite
				b.Pix[b.index(x, y)] = c
			default: // partly there: mix
				i := b.index(x, y)
				b.Pix[i] = Over(c, b.Pix[i])
			}
		}
	}
}

Two decisions are buried in that loop. The lookup walks a list instead of hashing a map, because a palette here is sixteen entries and the comparisons are cheaper than the hash would be. And the sampling is nearest neighbour with the source coordinate worked out in integers, with no filtering at all: a filtered pixel is a colour halfway between two palette entries, and the argument of this whole chapter is that no such colour exists. Drawn smaller than it was authored, a sprite loses whole rows and columns of itself, visibly, which is what the picture would have to be if a person drew it again by hand at that size.

$ go run ./cmd/looks -mode palette | tail -50
  the sheet's first cell, one character a pixel, each character a palette entry

    ................
    ................
    ................
    ................
    ..........0.....
    ..........0000..
    ...033333303f00.
    0002ccccc203220.
    .002bbb22200aa0.
    .0021122220000..
    ..0011111100....
    ...010000010....
    ....10....10....
    ...000....000...
    ................
    ................

  a genome whose look genes read 0.80 0.30 0.95 0.10
    coat green, markings rock on 3 of the 3 patches, part plain, drawn 16 pixels

     entry comes out   what it is on the sheet
         0        0   outline                    181A29 -> 181A29
         1       10   coat shadow                4C2F22 -> 254C39
         2       11   coat midtone               75563B -> 4C7841
         3       12   coat lit                   A1875A -> 90A858
        10        4   muzzle, the smallest patch 254C39 -> 323847
        11        5   flank                      4C7841 -> 5A6470
        12        6   the band down the back     90A858 -> 8A979E
        15       15   eye                        F5EBD7 -> F5EBD7

  the same cell with that substitution made

    ................
    ................
    ................
    ................
    ..........0.....
    ..........0000..
    ...0cccccc0cf00.
    000b66666b0cbb0.
    .00b555bbb00440.
    .00baabbbb0000..
    ..00aaaaaa00....
    ...0a00000a0....
    ....a0....a0....
    ...000....000...
    ................
    ................

The two pictures are the same picture. Every pixel that was there is still there, in the same place, and the character standing in each one has changed: b where 2 was, 6 where c was, 0 everywhere it was 0 before. A coat gene of 0.80 lands in the fourth of four bands, which is the green ramp, so the sheet's browns come out as greens. A marking gene of 0.30 lands in the second band, the rock ramp, and a coverage gene of 0.95 reaches all three patches, so the greens come out as greys. The eye and the outline never move.

A run printed as characters is not decoration here. A palette-indexed picture really is a grid of small integers, and printing it as one is the only way to see that a substitution is a substitution: no pixel moved, no pixel appeared, no pixel became a colour that is not on the list. The tests say the same thing about every look the world can produce rather than about this one.

$ go test -count=1 ./internal/gene/ -run 'TheIdentityGenomeIsDrawn|DrawnSizeIsThe|NoLookMakesA|AnUncoveredPatch|ALookGeneOfExactly' -v
=== RUN   TestTheIdentityGenomeIsDrawnAtTheCellSize
--- PASS: TestTheIdentityGenomeIsDrawnAtTheCellSize (0.00s)
=== RUN   TestDrawnSizeIsTheCubeRootOfBulk
--- PASS: TestDrawnSizeIsTheCubeRootOfBulk (0.00s)
=== RUN   TestNoLookMakesAColourTheValleyDoesNotHold
--- PASS: TestNoLookMakesAColourTheValleyDoesNotHold (0.00s)
=== RUN   TestAnUncoveredPatchTakesTheCoatsOwnTone
--- PASS: TestAnUncoveredPatchTakesTheCoatsOwnTone (0.00s)
=== RUN   TestALookGeneOfExactlyOneStaysInTheLastBand
--- PASS: TestALookGeneOfExactlyOneStaysInTheLastBand (0.00s)
PASS
ok  	theworld/internal/gene	0.004s

The third of those walks all sixty-four combinations of coat, marking hue and coverage the world can make, and asks of every one that all sixteen colours coming out of the table are colours the valley already holds, and that the outline and the eye have not moved. The fourth checks the disappearing patch: for every coat and every coverage, a patch the coverage does not reach is the coat's own tone of that brightness and nothing else.

Body mass and drawn width

The bulk factor is the first gene of the body block and it multiplies the mass of the animal's body. A creature at 4.00 is four times the mass of the founding row, pays four times the upkeep, and needs four times the grams to be born. It cannot be four times as wide on the screen, and the reason is arithmetic rather than taste.

∑ Math Interlude — mass is three lengths multiplied together

Take a cube one centimetre on a side. It holds one cubic centimetre. Now take a cube two centimetres on a side: it is twice as long, twice as deep and twice as tall, so it holds 2 × 2 × 2 = 8 cubic centimetres. Doubling the length multiplied the volume by eight, and if the two cubes are made of the same stuff, the second weighs eight times as much as the first.

Run that backwards. An animal of eight times the mass is twice as long, so the multiplier on length for a mass multiplied by four has to sit somewhere between one and two. It is the number that gives 4 when multiplied by itself three times, and that number is 1.5874 to four places. It has a name, the cube root of 4, and one way to check it is by hand: 1.5874 × 1.5874 = 2.5198, and 2.5198 × 1.5874 = 3.9999, which is 4 to the places printed here.

The sprite is sixteen pixels on a side, so the drawn width of a body of bulk factor m is 16 × the cube root of m, rounded to a whole pixel because there is no such thing as three fifths of a pixel. At the bottom of the allowed range, m = 0.25, the cube root is 0.6300 and the width is 16 × 0.6300 = 10.0794, which rounds to 10. At the top, m = 4.00, it is 16 × 1.5874 = 25.3984, which rounds to 25. So the whole range of bodies this world allows, sixteen times heaviest to lightest, draws between ten pixels and twenty-five.

drawn width = round(16 × m1/3)

mthe bulk factor, a multiplier on the founding row's body mass, clamped to 0.25–4.00
m1/3the cube root of m: the number that gives m when multiplied by itself three times
16the cell size: how many pixels a side the sheet was drawn at
roundto the nearest whole number, halves going up
bandthe run of bulk factors that all round to one drawn width
▣ Build · stage 5 — the size law, and reading it backwards
// Cell is how many pixels the creature sheet is drawn at, on a side.
const Cell = 16

func Pixels(bulk float64) int {
	n := int(math.Round(float64(Cell) * math.Cbrt(bulk)))
	if n < 1 {
		n = 1
	}
	return n
}
$ go run ./cmd/looks -mode size
looks: drawn size is the cell scaled by the cube root of the bulk factor

  the sheet is 16 pixels on a side and a bulk factor runs 0.25 to 4.00

      bulk  cube root  16 x that    drawn  against 1
      0.25     0.6300    10.0794       10     0.6250
      0.50     0.7937    12.6992       13     0.8125
      0.80     0.9283    14.8531       15     0.9375
      1.00     1.0000    16.0000       16     1.0000
      1.25     1.0772    17.2355       17     1.0625
      2.00     1.2599    20.1587       20     1.2500
      3.00     1.4422    23.0760       23     1.4375
      4.00     1.5874    25.3984       25     1.5625

  every width the law can produce, and the bulk it takes to reach it
     drawn    from bulk      to bulk       band
        10       0.2500       0.2826     0.0326
        11       0.2826       0.3713     0.0887
        12       0.3713       0.4768     0.1055
        13       0.4768       0.6007     0.1238
        14       0.6007       0.7443     0.1436
        15       0.7443       0.9091     0.1649
        16       0.9091       1.0967     0.1876
        17       1.0967       1.3084     0.2117
        18       1.3084       1.5458     0.2374
        19       1.5458       1.8103     0.2645
        20       1.8103       2.1033     0.2930
        21       2.1033       2.4264     0.3231
        22       2.4264       2.7809     0.3546
        23       2.7809       3.1684     0.3875
        24       3.1684       3.5904     0.4219
        25       3.5904       4.0000     0.4096

  16 widths over a range of 3.75; the band at 11 pixels is 0.0887 wide and the one
  at 24 is 0.4219, so one pixel of width costs 4.76 times the bulk at the top

The second table is the law read the other way round, and it is the part a reader can use. Sixteen distinct widths cover the whole range of bodies. Near a bulk factor of 1.00 it takes about 0.19 of change to move the picture by a single pixel, so a mutation that nudges bulk by five percent of its range moves the drawn animal by one pixel about half the time. Up at the heavy end it takes 0.42, and down at the light end 0.089. The same one pixel of width costs nearly five times as much body at the top of the range as at the bottom, which is the cube root doing exactly what a cube root does.

Ten pixels is the size the part variants have to survive, because that is what the lightest bodies this world allows are drawn at: a sixteen-pixel sprite with six rows and six columns of itself thrown away.

$ go run ./cmd/looks -mode parts
looks: the four part variants, at the size they were drawn and at 10 pixels

  part            row   drawn 16     drawn 10
  plain             0         88           33
  crest             1        100           36
  horns             2         97           35
  longneck          3         95           35

  the four at 16 pixels, side by side

    ....................................................................
    ...........................................0....0............0......
    .......................0...................00..00............0000...
    ......................030...................3..3.............03f00..
    ..........0...........0330.0................0..0.............03220..
    ..........0000........0330.0000.............0000............000aa0..
    ...033333303f00.....033333303f00.....033333303f00.....03333300000...
    0002ccccc203220..0002ccccc203220..0002ccccc203220..0002ccccc030.....
    .002bbb22200aa0...002bbb22200aa0...002bbb22200aa0...002bbb22030.....
    .0021122220000....0021122220000....0021122220000....00211222030.....
    ..0011111100.......0011111100.......0011111100.......0011111100.....
    ...010000010........010000010........010000010........010000010.....
    ....10....10.........10....10.........10....10.........10....10.....
    ...000....000.......000....000.......000....000.......000....000....
    ....................................................................
    ....................................................................

  and at 10

    ............................................
    ............................0..0............
    ...............3........................3f0.
    ...............30.......................320.
    ..033333f0...033333f0...033333f0...0333000..
    .02bb220a0..02bb220a0..02bb220a0..02bb200...
    .02122200...02122200...02122200...0212200...
    ..010000.....010000.....010000.....010000...
    ...1...0......1...0......1...0......1...0...
    ............................................

  outline pixels over the four: 198 at 16, 63 at 10

The lower block shows what downscaling costs. Of a hundred and ninety-eight outline pixels across the four variants, sixty-three survive at ten: the ink border that holds a sprite together at full size loses more than half of itself at the smallest bodies this world can grow. What survives is the silhouette. The sail on row 1 is still a bump over the back, the horns on row 2 are still two marks over the crown, the raised head on row 3 is still raised. All four remain distinguishable at ten pixels, but the horn stems lose their connection to the head and the feet lose their last row. Sampling decides which details survive. Check the smaller picture whenever you edit a mask; counting the source pixels alone cannot prove that a feature remains readable.

Four species from one ancestor

Now the animals. A run for this needs four lineages far enough apart that no two of them may breed, and it needs them to have a common ancestor, and the valley cannot supply either: two centuries of it produce one species, because the ground saturates and a six-cell mate box makes the whole valley one neighbourhood. So the run is a bench. One founding genome; two hundred births of a single line to get an ancestor to name; then four branches off that animal, each of them two thousand more births of copy, mutate and grow, all four taking their turn at every birth and sharing one innovation table for the tick they are born on.

▣ Build · stage 6 — one line, then four
// cmd/looks/kin.go
for j := 1; j <= limb; j++ {
	g.Mint.Now(stem + j)
	for b := range g.Limbs {
		l := g.Limbs[b]
		kid, _ := gene.Weave(l.At, l.At, gene.Both.Sex, mix, false)
		gene.Usual.Mutate(kid, mut)
		gene.Often.Grow(kid, g.Mint, grow)
		g.next++
		g.Roll.Birth(g.next, 1, stem+j, l.ID, 0, kid)
		g.Births++
		g.Limbs[b] = Limb{At: kid, ID: g.next}
	}
}

Three operators and an archive write, which is the valley's own birth with the ground, the store and the mate taken out. The four branches read one mutation stream and one growth stream between them, in a fixed order, so the run is one history and not four. Every birth goes into the archive as it happens.

$ go run ./cmd/looks -mode kin
looks: one line branched four ways, and what the four ended up looking like

  seed 5, 200 births of one line, then 2000 more down each of four branches
  every birth is one parent copied, mutated and grown; 8201 in all
  creature 201 is the last of the stem and the ancestor all four run back to

  creature  species    links   genes     coat  marking patches       part    bulk   drawn
       201        -      149     176    green    water       3      crest  2.0973      20
      8198        1      267     322    green     soil       2      horns  0.6161      14
      8199        2      255     310    green     rock       2   longneck  1.9155      20
      8200        3      248     303    water     soil       3      crest  0.5810      13
      8201        4      259     314     rock     soil       1      plain  3.6636      25

  the ancestor is dead and is in no species: the register holds the living

  how far apart the four are, against a threshold of 3.00
            pair      E      D      N      Wbar      Bbar         d  may breed
   8198 and 8199      2    302    267    2.8916    1.2943    3.5895  no
   8198 and 8200      1    302    267    2.5613    0.8843    3.0437  no
   8198 and 8201      3    305    267    2.8495    2.0529    4.3462  no
   8199 and 8200      3    290    255    2.7483    1.1948    3.4432  no
   8199 and 8201      4    294    259    2.9111    1.3094    3.6244  no
   8200 and 8201      3    286    259    2.4861    1.3311    3.4414  no
  6 of the 6 pairs are outside the threshold

  and how far each of them is from the ancestor they share
            pair      E      D      N      Wbar      Bbar         d  may breed
    201 and 8198    141     23    267    2.6198    1.0185    2.6807  yes
    201 and 8199    128     22    255    2.7204    1.2522    2.9286  yes
    201 and 8200    122     23    248    2.4989    0.6312    2.2154  yes
    201 and 8201    130     20    259    2.6381    1.2463    2.8807  yes

Six pairs, six refusals, four species. The closest of the six clears the threshold by four hundredths, which needs saying plainly: two of these four are barely two kinds of animal, and one fewer birth down either branch might have left them one.

The second table is the part that catches people, and it is not an error. Every one of the four is inside the threshold from the ancestor they share. If creature 201 were somehow still alive it could breed with all four of its descendants, none of which can breed with each other. Relatedness measured as a distance does not pass along a chain: A near B and B near C says nothing about A and C, and a rule built out of a radius will always have that property. The reason nothing in this world trips over it is that the ancestor is dead. It stopped two thousand births ago and it is in no species, because the species register holds living animals.

Which means the portrait has to come out of the archive. That is what the archive is for: a creature's genome is written down once when it is made and never touched again, so an animal that has been dead for two thousand births is still on disk with every number it inherited, look genes included. Nothing in the running world reads that file. Somebody sitting down afterwards does.

▣ Build · stage 7 — five animals, painted out of a file

The drawing mode runs the same branch, writes every birth to an archive, closes it, opens it again as a stranger would, and asks it five questions: the ancestor's record and the four tips'. The genomes it paints are rebuilt out of JSON, not carried over from the run that made them.

$ go run ./cmd/looks -mode draw -shot assets/frames/four-lineages.png
looks: the four drawn out of the archive, and not out of a pool

  draw-68.jsonl: 8201 born records, 106783750 bytes, sha256 687aadcb54100bde
  read back: 8201 records, 8201 of them with a genome in
  6 of the 6 pairs meet at creature 201, born on tick 200, 2000 births up from each
  creature 201 stands 200 births from the founder; the four stand 2200

  creature  species     coat  marking patches       part    bulk   drawn
       201        -    green    water       3      crest  2.0973      20
      8198        1    green     soil       2      horns  0.6161      14
      8199        2    green     rock       2   longneck  1.9155      20
      8200        3    water     soil       3      crest  0.5810      13
      8201        4     rock     soil       1      plain  3.6636      25

  the card 135 by 40 pixels, 5401bdd1866049a70d23f65912ce779a2e846ef4cb0f9c5e417389875cd08292
  wrote assets/frames/four-lineages.png at 4 times, 540 by 160

The five rows are the same five rows the previous run printed, which is the claim being checked: the coat, the markings, the coverage, the part and the drawn width all came back out of a hundred and seven megabytes of JSON identical to the numbers the run had in memory. The pedigree walk is the archive's own: every one of the six pairs of tips meets at creature 201, two thousand births up from each of them, and 201 itself stands two hundred births from the founder that opened the file.

A pixel drawing of five small four-legged animals standing in a row on a band of brown soil against a dark blue-grey sky. The leftmost, set apart behind a thin vertical line, is a green animal of middling size with a raised sail along its back and a blue band across it. The other four are numbered 1 to 4 above them: a small green one with two horns on its head, a taller green one with its head raised on a neck, a very small blue one with a brown band down its back, and a large grey one with no ornament at all, nearly twice as wide as the smallest.
Figure 68.1assets/frames/four-lineages.png: creature 201 on the left, behind the rule, and the four species descended from it. Every pixel of all five is one of the valley's sixteen colours, and no two of them were drawn from the same row of the sheet in the same substitution at the same size. The ancestor was a green crested animal twenty pixels across; of its four descendants two kept the coat, one kept the crest, and none kept both.

Read the picture against the table and every column of the table is on it. Species 4 is the widest thing in the frame at twenty-five pixels, and it has no ornament at all: a plain grey animal at a bulk factor of 3.66. Species 3 is thirteen pixels, a water coat with all three patches marked in soil, and it is the same crested variant as the ancestor wearing an entirely different set of colours. Species 1 and 2 are both green and are not remotely each other: one is fourteen pixels with horns and two marked patches in soil, the other twenty pixels with a raised head and its two patches in rock. Four coats was never going to separate four species on colour alone, and it did not have to.

⚠ Worked failure — the substitution that ran one step too late

Here is the shortcut. The renderer already has a blit that copies a frame onto the framebuffer. Rather than write a second one, blit the sprite normally and then walk the finished picture, putting every pixel through the substitution table. Same table, same answers, one loop instead of a new argument on the hot path. It is a reasonable thing to reach for and it is wrong, and the flag on the bench runs it so the wrongness has a number on it.

$ go run ./cmd/looks -mode draw -late | tail -4
  the card 135 by 40 pixels, 5401bdd1866049a70d23f65912ce779a2e846ef4cb0f9c5e417389875cd08292
  the substitution run over the finished card instead: 936 pixels moved,
  777 of them ground and sky that no creature was ever drawn on
  the card 135 by 40 pixels, 1929500362817840799ea29036ca77742d3dc0e8c2c2d2207cc15842aa5e98cc

Nine hundred and thirty-six of the card's five thousand four hundred pixels change colour, and seven hundred and seventy-seven of them are ground and sky. The card's soil band is drawn in palette entry 2, which is the sheet's coat midtone, so the last creature's coat substitution catches it and the ground comes out in the grey of that animal's coat. Walk back from the symptom: the framebuffer does not know where its pixels came from. It holds colours, not palette entries with a history, and entry 2 in the ground and entry 2 in a sprite are the same thirty-two bits by the time they are in it. A substitution that runs on the framebuffer therefore recolours whatever else in the world happens to share a colour with the animal, and there are only sixteen colours, so something always does.

The other two hundred and forty-three moved pixels are the same mistake landing on the creatures: a coat already substituted into the green ramp is substituted a second time, because green entries are marking entries on the sheet, and every pixel that came out soil is read as a coat entry all over again. The fix is the argument on the blit. A pixel can only be substituted while the code still knows it came from the sheet, and the last moment that is true is inside the loop that reads it.

Why appearance is computed

The general rule underneath this chapter is older than any of it. A genotype is what is stored and inherited; a phenotype is what the world sees, and it is worked out from the genotype and the situation every time it is needed. Nothing in biology stores an animal's colour anywhere except in the instructions for making it, and nothing in this program does either. The stamped body row is computed from the genome. The controller is built from the genome. The look is derived from the genome, at the moment of drawing, and thrown away as soon as the pixels are down.

The payoff for that discipline is that a whole class of bug cannot happen. There is no state to get out of step, no cache to invalidate, no field for a well-meaning line of simulation code to assign to, and no possibility at all of an animal whose picture says one thing and whose genome says another. It also means the four look genes cost the valley precisely nothing while nothing is being drawn: a headless run doing two centuries of ticks never calls Shown once.

The road from four genes to pixels, and the two places nothing is allowed to go A left-to-right chain of four boxes. The first is the genome, listing coat, marking, coverage, part and bulk as numbers. An arrow leads to the second box, the derivation, which holds a ramp, a marking ramp, a count of patches, a sheet row and a drawn width of sixteen pixels. An arrow leads from that to the blit, which reads the frame, swaps the entry and writes the pixel, and from the blit to the framebuffer, which holds colours with no palette entries and no history behind them. Two dashed crossed arrows mark what is not allowed: one from the derivation down to the creature struct, which names no colour and no size, and one pointing at the framebuffer from below, marking that no substitution is ever run there. DERIVED EVERY TIME, STORED NOWHERE GENOME coat 0.80 marking 0.30 coverage 0.95 part 0.10 bulk 1.00 SHOWN green ramp rock markings all 3 patches sheet row 0 16 pixels THE BLIT read the frame swap the entry write the pixel FRAMEBUFFER colours only no entries no history nothing is written back beast.Beast no colour, no size no substitution here A PIXEL CAN ONLY BE SWAPPED WHILE THE CODE STILL KNOWS WHICH SHEET IT CAME FROM
Figure 68.2 — the road from four genes to pixels. The two crossed arrows are the rules: nothing computed on the way is written back onto the creature, and the substitution happens inside the blit, where a pixel still has a sheet entry behind it rather than only a colour.

One more thing this buys, and it is the reason the derivation lives in the package that owns genomes rather than in the renderer. The renderer knows about buffers, rectangles and colours. It does not know what a creature is, what a gene is or that a valley exists, and after this chapter it still does not: what it gained is a blit that takes two lists of colours and a destination size. Everything about coats, markings, coverage and parts is in the package that already holds every genome the run has made, which is where a question about what a genome means belongs.

✓ Checkpoint — a palette, a table and five animals
  • You can say which eight of the valley's sixteen entries the creature sheet is drawn in, and why the coat takes three of them and not one.
  • Given a coat gene, a marking gene and a coverage gene, You can write out the sixteen-entry table by hand and say which entries are fixed points.
  • You can explain why an unmarked patch comes out in the coat's own tone of the same brightness instead of being drawn and then painted over.
  • Given a bulk factor You can work out the drawn width to the pixel, and say roughly how much a body has to change to move the picture by one pixel near 1.00 and near 4.00.
  • Reading the six-pair table, You can say why four animals can be four species while every one of them is still inside the threshold from the ancestor they share.
  • You can say what goes wrong when the substitution runs on the framebuffer instead of inside the blit, and name which parts of a finished frame it damages before running it.
⚡ Exercises — try first, then reveal
Exercise 1 — draw an animal that is all one colour. There is a combination of look genes that produces a creature with no visible markings at all, and another that produces one whose markings are invisible even at full coverage. Find both from the table alone, then check the first by running cmd/looks and reading the printed characters.

The first is any coverage gene under 0.25, which lands in band 0: no patch is reached, so all three marking entries come out as the coat's own tones and the sprite is drawn in three colours plus ink and bone. The second is a coat gene and a marking gene that land in the same band — both under 0.25, say, or both over 0.75. Then the marking ramp is the coat ramp, every patch is substituted for the coat entry of its own brightness, and full coverage looks exactly like none. Sixteen combinations of the two hues, four of them collisions.

The palette mode takes the four genes as flags, so the collision can be run rather than argued about. Ask it for a soil coat, soil markings and near-full coverage: the table comes back with entries 10, 11 and 12 pointing at 1, 2 and 3, and the drawn cell holds nothing but coat tones, ink and one pixel of bone. Every marking on the sheet has been substituted for the coat entry of its own brightness, and a creature at full coverage is drawn exactly like one carrying none.

$ go run ./cmd/looks -mode palette -coat 0.10 -mark 0.10 -cover 0.99 | tail -24
         3        3   coat lit                   A1875A -> A1875A
        10        1   muzzle, the smallest patch 254C39 -> 4C2F22
        11        2   flank                      4C7841 -> 75563B
        12        3   the band down the back     90A858 -> A1875A
        15       15   eye                        F5EBD7 -> F5EBD7

  the same cell with that substitution made

    ................
    ................
    ................
    ..........0000..
    .........033330.
    ..000000003f330.
    .03333333033110.
    .03333333321110.
    .0222222222220..
    .022222222220...
    ..0221111110....
    ...01...01......
    ...01...01......
    ...00...00......
    ................
    ................
Exercise 2 — prove the new blit is the old one. The sampling blit and the plain blit have to agree exactly when the destination is the same size as the source, or every frame in this volume is off by a row somewhere. Write the test that says so, and one that says a colour the table was not given comes back untouched.

Build a small sheet in memory so the test needs no file, blit one frame of it both ways onto two buffers pre-filled with the same colour, and compare the two framebuffer hashes. The hash is the right comparison: it catches a one-pixel disagreement anywhere in the rectangle, and it is the same check the renderer's golden images have used since the snapshot chapter. The second test asks Remap.Of for a colour that is in neither list and demands it back unchanged, then blits a sheet cell holding that colour and checks it landed.

$ go test -count=1 ./internal/render/ -run 'BlitMapAtItsOwn|AnEntryOutsideThe' -v
=== RUN   TestBlitMapAtItsOwnSizeIsTheOrdinaryBlit
--- PASS: TestBlitMapAtItsOwnSizeIsTheOrdinaryBlit (0.00s)
=== RUN   TestAnEntryOutsideThePaletteIsLeftAlone
--- PASS: TestAnEntryOutsideThePaletteIsLeftAlone (0.00s)
PASS
ok  	theworld/internal/render	0.004s
Exercise 3 — find out when the four became four. The branches were run for two thousand births each and ended as four species. Work out roughly where along the way that happened, and say why the answer is not a single birth number.

Shorten the branches and read the six-pair table. At a thousand births five of the six pairs are already outside the threshold and one is not: creatures 4199 and 4200 sit at 2.5159 and could still have a child together, so the run is not four species yet and is not one either.

The second table is the sharper answer. At a thousand births the fourth branch stands 3.0536 from the ancestor, outside the threshold; two thousand births down the same branch it stands 2.8807, inside it again. The distance is not a ratchet. The body-factor term wanders in both directions, so a pair can cross the line, fall back under it and cross again, and "when did they become four" has no single birth number in it.

$ go run ./cmd/looks -mode kin -limb 1000 | tail -14
   4198 and 4199      1    192    215    2.8973    1.3988    3.4554  no
   4198 and 4200      1    158    208    2.5686    1.4228    3.2147  no
   4198 and 4201      4    184    212    2.8328    1.0479    3.0678  no
   4199 and 4200      1    173    215    2.5682    0.6793    2.5159  yes
   4199 and 4201      2    205    215    2.4223    1.2787    3.2104  no
   4200 and 4201      5    162    212    2.6612    1.5647    3.4169  no
  5 of the 6 pairs are outside the threshold

  and how far each of them is from the ancestor they share
            pair      E      D      N      Wbar      Bbar         d  may breed
    201 and 4198     76     17    208    2.5289    0.9045    2.3632  yes
    201 and 4199     87     21    215    2.3966    0.9562    2.4172  yes
    201 and 4200     56     12    193    2.6634    1.1026    2.5203  yes
    201 and 4201     81     18    212    2.7967    1.4679    3.0536  no

Every number this volume has produced so far has come out of one seed run once. The four animals in the picture are a fact about seed 5 and about the order the branches took their turns in, and both of those are decisions somebody made, not facts about evolution. The next thing to establish is that they are decisions the run keeps: that the same seed, put through the same ordering rules twice, gives back the same eight thousand births, the same four species and the same card, to the byte.