Drawing the Neighbours
Three people with seven fields each and no face among them
Halla is a name, a body number, two sentences of persona, a law profile, a home on cell 8,5, two hundred grams and a founding tick, and nothing on that row is a thing a renderer can draw. Ander is the same seven fields with other values in them. So is Mose. The last chapter ended with a line the hero model wrote for Ander kept in a field nothing reads, because a line that reaches a reader has to be said from a face and said in a box, and the village has neither. The face comes first, and it comes with a rule it has to obey before a pixel of it is placed.
The obvious way to give three people faces is to open an editor, draw three
portraits, save them as three files, and load each one by the villager's name. That
works for three. It puts a filename on the row, which is a field a line of
simulation code could assign to; it makes the fourth villager an afternoon's work and
the twentieth a week's; and it leaves three pictures in assets/ that no
program can rebuild, which volume 6 named as the thing a contract cannot check.
Nothing about a hand-saved portrait says it is the portrait of the row it is named
after, and nothing can, because the row had no part in making it. A villager's
face is arithmetic over its row. The parts are drawn once, on one sheet; which parts, and
in which colours, is chosen off the record by a fold in a fixed order; the same row always
makes the same face; and nothing about the face is stored anywhere.
Chapter 68 met this problem for animals and answered it with a genome: a creature's coat, markings, part and size are worked out from four numbers it was born with, every time it is drawn, and stored nowhere. A villager has no genome. What it has is the roll, and the roll is a record somebody wrote down on purpose, which is a better thing to draw a face from than four mutated floats. That is chapter 68's rule with the genome taken out and the roll put in its place, and this page says so once. No model is asked what Halla looks like, exactly as no model was asked what a genome looks like: a face is arithmetic over a row, and arithmetic replays.
By the end of the page there is a sheet of eighteen parts and a strip of seven colours, both written by a program and compared byte for byte with the pair the book ships; a fold short enough to work on paper that turns a name and a home cell into a number under 3072, and a peel that turns the number into a head, a pair of eyes, a mouth, a hair and a colouring; the three people of the roll composed twice from two reads of the file and found identical; and the same three composed off a random stream instead, twice, so that the thing the fold prevents is on the page and not in a warning.
Eighteen parts in seven of the sixteen, drawn as letters
A face here is sixteen pixels on a side. That is the cell every sheet in this book is cut on, the walker's, the tileset's, the creature sheet's, so the loader, the frame arithmetic and the blit all take the sheet as it is; and at sixteen a face has room for what a face needs. An eye is three pixels, a white on each side of a pupil, and two eyes two pixels apart is eight; a head ten wide leaves a column of outline each side of that and two columns of hair outside the outline; a mouth is four pixels one row high; and the rows above the eyes hold a brow and a crown, the rows below a nose, a mouth, a chin and a neck. Twelve was tried, by sampling, and the section after this one says what it lost. Twenty-four was not, because nothing in the list above wants a pixel it does not have.
The face is four parts laid over one another: a head, a pair of eyes, a mouth, and the hair, in that order, so the hair falls over the brow and not under it. Each part is one cell of one sheet, one row a kind: four heads, four pairs of eyes, four mouths, six hairs. Eighteen cells, drawn once. The heads share two lines so that the other parts can be drawn once and land on all of them: the eye line is row 7 and the mouth line is row 10; and every hair is drawn wide enough to cover the widest crown, so a hair seats on any of them. What the heads differ in is the skull's width, the jaw and the ears, because that is where one face differs from another: round, long, square and narrow.
Every part is drawn in seven of the valley's sixteen colours, and the seven are a choice about what a face is made of. Ink is the outline, the pupil and the mouth, and it is a fixed point of every colouring: an edge is ink whoever wears it. The three entries of the soil ramp are the skin, shadow, midtone and lit, for the same reason a coat took a whole ramp in chapter 68: a face in one flat colour has no form in it, and a face in three has a lit brow, a midtone cheek and a shaded jaw that move together when the colouring changes. The rock ramp's shadow and midtone are the hair, dark for the sides and lit for the crown, because the light in this world has come from the upper left since the walker and the crown is what it reaches first. Bone is the eye's white and the teeth, and it is the other fixed point.
Every part below is drawn as characters and checked as pixels at four times, which
needs no editor at all. If you would rather place pixels with a pencil, LibreSprite
(libresprite.github.io) or Pixelorama (github.com/Orama-Interactive/Pixelorama)
will load assets/palette/valley-16.gpl as a palette, which reduces the
colour picker to exactly the sixteen this world allows; draw a ninety-six by
sixty-four sheet with a sixteen-pixel grid on, export it with a real alpha
channel, and carry each cell into the table a character at a time. The table is
what ships, because the table is what a program can rebuild.
Create internal/village/face.go. The counts are the sheet's grammar,
the way TileCols and CellSoilA were the tileset's: nothing
downstream parses folk.png to find out what is on it. They live in
village and not in render, because the question they answer
is about a person, and render does not know what a person is.
// internal/village/face.go
package village
import (
"theworld/internal/gene"
"theworld/internal/render"
)
// A villager's face is worked out from the row every time it is drawn
// and is stored nowhere. Nothing on Person names a head, a hair or a
// colour, exactly as nothing on beast.Beast names a coat: appearance is
// derived from the record, and a record that has not changed cannot be
// drawn two ways.
//
// The face is four parts laid over one another, each one cell of
// assets/sprites/folk.png: a head, a pair of eyes, a mouth, and the
// hair, in that order, so the hair falls over the brow rather than
// under it. The sheet holds one row a kind, Across cells wide, and the
// rows are in the order the parts are laid down.
const (
Cell = 16 // a part is this many pixels on a side, and so is a face
Heads = 4
Eyes = 4
Mouths = 4
Hairs = 6
Tints = 8
Faces = Heads * Eyes * Mouths * Hairs * Tints // 3072
Across = Hairs // the longest row is the sheet's width
)
// The rows of the sheet, one kind of part each, top to bottom.
const (
HeadRow = iota
EyesRow
MouthRow
HairRow
Rows
)
// Radix is how many of each part there are, in the order a face's
// number is peeled: the head is the lowest digit and the colouring the
// highest. A face is one number under Faces, and this is its base.
var Radix = [5]int{Heads, Eyes, Mouths, Hairs, Tints}
// Strip is the sheet's own palette: seven of the valley's sixteen, in
// the order the strip lists them. Every pixel of every part on the
// sheet is one of these, and a colouring is a substitution on them.
var Strip = [7]int{gene.Ink, gene.SoilD, gene.SoilM, gene.SoilL, gene.RockD, gene.RockM, gene.Bone}
// What each entry of the strip is on the sheet, by its position in
// Strip. Line and White are fixed points of every colouring: an
// outline is ink whoever wears it, and an eye's white is bone.
const (
Line = iota // every outline, every pupil, every mouth
SkinD // the skin's shadow: under the chin, the far cheek, the nose's side
SkinM // most of the face
SkinL // the lit brow and cheek
HairD // the hair's sides and underside
HairL // the crown, which the light reaches first
White // the eye's white, and teeth
)
village now imports gene, and the import is for one thing:
gene.Sixteen is where the palette has been read out of its file since
the creature sheet, and a palette written down twice can disagree with itself.
Nothing about a genome crosses. Tints is eight and Faces is
3072, and both are explained further down, where the colourings are; the constants
are here because the sheet is drawn to them.
Create cmd/mkfolk/art.go. Every pixel is a letter, a letter is an entry
of the strip, and a space is a pixel that is not there, so a part is mostly space:
a pair of eyes is six pixels of a cell of two hundred and fifty-six, and everything
else in the cell lets the head show through. Four of the eighteen are printed here
in full; the other fourteen are in the same form, and figure 99.1 shows all of them.
// cmd/mkfolk/art.go
//
// o outline ink, never remapped: every edge, every pupil, every mouth
// s skin shadow the far cheek, the side of the nose, under the chin
// k skin midtone most of the face
// l skin lit the brow and the near cheek, where the light lands first
// h hair dark the sides and the underside
// H hair lit the crown
// e white the eye's white, and teeth
// ...
// roundHead is the founding face: a skull that closes on row 2, ears
// on rows 6 and 7, cheeks that come in one column a row from row 10,
// and a chin on row 12. The eye line is row 7 and the mouth line row
// 10, which every head keeps, and the near cheek carries the one lit
// pixel the hair does not cover.
var roundHead = []string{
" ",
" ",
" oooooo ",
" olkkkkko ",
" olkkkkkkso ",
" okkkkkkkso ",
" okkkkkkkkkso ",
" okkkkkkkkkso ",
" okkkkskkso ",
" olkkkskkso ",
" okkkkkkkso ",
" okkkksso ",
" oooooo ",
" osso ",
" okko ",
" oooo ",
}
// ...
// openEyes are six pixels: white, pupil, white, twice, two pixels
// apart on row 7. Three pixels is the least an eye can be and still
// have a white on both sides of its pupil.
var openEyes = []string{
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" eoe eoe ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
}
// ...
// smileMouth is the line with a pixel raised at each end, on row 9.
var smileMouth = []string{
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" o o ",
" oooo ",
" ",
" ",
" ",
" ",
" ",
}
// ...
// longHair falls to row 13 down both sides, outside the face at
// columns 2 and 3 and 12 and 13. It once sat one column further in and
// took the outer white off each eye; a test in this directory now
// composes every hair over every pair of eyes and refuses that.
var longHair = []string{
" ",
" oooooo ",
" oHHHHhho ",
" oHHHHhhhhhho ",
" oHH hho ",
" oHh hho ",
" oH ho ",
" oH ho ",
" oH ho ",
" oh ho ",
" oh ho ",
" oh ho ",
" oho oho ",
" ooo ooo ",
" ",
" ",
}
// ...
Read the round head against the rules. The outline is closed all the way round, ink on every boundary pixel, so the face sits on any background; the far cheek at column 11 and the neck under the chin are shadow, the second of those because the chin is above it and not because of the angle; the nose is two pixels of shadow down its right side at rows 8 and 9; and the lit pixels are the upper-left brow and the near cheek at row 9, which is the one lit pixel a fringe cannot reach. The long hair has its own outline and its own row 1, so it covers the head's row 2 instead of joining it, and its sides run down columns 2 and 3, outside the face, with a lit strand on the near side where the crown's light continues.
Create cmd/mkfolk/main.go. Painting a part into the sheet is a lookup and
a Set, and the lookup is the part that matters: a character with no
strip entry behind it stops the program instead of putting a colour nobody chose
into an asset. The array types are the second check, and it is free: a row with
one part too many or too few does not compile.
// cmd/mkfolk/main.go
var key = []struct {
ch byte
at int
what string
}{
{'o', village.Line, "outline"},
{'s', village.SkinD, "skin shadow"},
{'k', village.SkinM, "skin midtone"},
{'l', village.SkinL, "skin lit"},
{'h', village.HairD, "hair dark"},
{'H', village.HairL, "hair lit"},
{'e', village.White, "white"},
}
// The four rows of the sheet, each the parts of one kind in the order
// the row holds them, and what each part is called when a run has to
// say which it picked. The array sizes are the package's counts, so a
// row with one part too many or too few does not compile.
var (
heads = [village.Heads][]string{roundHead, longHead, squareHead, narrowHead}
eyes = [village.Eyes][]string{openEyes, asideEyes, wideEyes, liddedEyes}
mouths = [village.Mouths][]string{lineMouth, smileMouth, frownMouth, grinMouth}
hairs = [village.Hairs][]string{cropHair, longHair, bunHair, fringeHair, sweptHair, bareHair}
// ...
)
// ...
// colour is the valley colour a strip position is drawn in.
func colour(pos int) render.Color {
return gene.Sixteen[village.Strip[pos]]
}
// ...
// paint writes one mask into the sheet at a cell.
func paint(sh *render.Buffer, col, row int, mask []string) {
if len(mask) != village.Cell {
die(fmt.Errorf("row %d cell %d has %d rows, want %d", row, col, len(mask), village.Cell))
}
for y, line := range mask {
if len(line) != village.Cell {
die(fmt.Errorf("row %d cell %d line %d is %d characters, want %d", row, col, y, len(line), village.Cell))
}
for x := 0; x < village.Cell; x++ {
ch := line[x]
if ch == ' ' {
continue
}
pos, ok := at(ch)
if !ok {
die(fmt.Errorf("row %d cell %d line %d: no palette entry for %q", row, col, y, ch))
}
sh.Set(col*village.Cell+x, row*village.Cell+y, colour(pos))
}
}
}
// ...
// build paints the whole sheet and the strip.
func build() (*render.Buffer, *render.Buffer) {
sheet := render.NewBuffer(village.Across*village.Cell, village.Rows*village.Cell)
for row, parts := range rows() {
for col, mask := range parts {
paint(sheet, col, row, mask)
}
}
strip := render.NewBuffer(len(key), 1)
for i, k := range key {
strip.Set(i, 0, colour(k.at))
}
return sheet, strip
}
$ go run ./cmd/mkfolk -to .
mkfolk: folk.png, 96x64, 4 rows of parts across 6 cells at 16 pixels, 845 bytes
pixels sha256 9bf56629912881799d6e6403038a4f2183defe4aa60ea47237a3913925621b76
the seven entries the sheet is drawn in, by their index in the valley's sixteen
o entry 0 181A29 outline
s entry 1 4C2F22 skin shadow
k entry 2 75563B skin midtone
l entry 3 A1875A skin lit
h entry 4 323847 hair dark
H entry 5 5A6470 hair lit
e entry 15 F5EBD7 white
part drawn outline skin hair white
round head 114 38 76 0 0
long head 104 38 66 0 0
square head 124 44 80 0 0
narrow head 109 36 73 0 0
open eye 6 2 0 0 4
aside eye 6 2 0 0 4
wide eye 12 2 0 0 10
lidded eye 12 8 0 0 4
line mouth 4 4 0 0 0
smile mouth 6 6 0 0 0
frown mouth 6 6 0 0 0
grin mouth 6 2 0 0 4
crop hair 34 14 0 20 0
long hair 74 36 0 38 0
bun hair 36 14 0 22 0
fringe hair 68 26 0 42 0
swept hair 34 13 0 21 0
bare hair 16 8 0 8 0
3072 faces the sheet can make: 4 heads x 4 eyes x 4 mouths x 6 hair x 8 colourings
mkfolk: folk-7.png, 7x1, 7 entries, 92 bytes
pixels sha256 50f170e5c379f76d3762ba89ab2a49e64b3b5dbd26db13ae70ef3ca13d52c0a8
pixels on the sheet the strip does not hold: 0
folk.png is byte for byte assets/sprites/folk.png
folk-7.png is byte for byte assets/palette/folk-7.png
Two files come out. The sheet is ninety-six pixels by sixty-four: six cells across
and four rows down, with the two cells the shorter rows do not use left empty, and
the count of what each row holds in the code and not in the picture. The strip is
seven pixels by one, one pixel an entry, the form the valley palette and the
creature strip have shipped in since they were chosen, so that the sheet's
palette is a file a reader can diff. Run with -to naming a directory,
the program writes the pair there and compares them with the pair the book ships,
and the last two lines are the whole reason the sheet is code: nobody has to trust
that the committed file is what the table makes, because the program says so every
time it runs. The tally is what the parts weigh. A head is a hundred and four to a
hundred and twenty-four pixels; the other three kinds together add between
twenty-six and ninety-two, and the smallest part on the sheet, the line of a mouth,
is four.
The sheet is mostly not there, so the picture of it is the sheet laid on a field
of open water with a line of rock between the cells, written out at four times by
-mode parts. The field and the rule are entries of the sixteen no face
is drawn in, so nothing in the picture is a part that is not one.
$ go run ./cmd/mkfolk -mode parts -shot assets/frames/folk-parts.png
mkfolk: the sheet on a field, one cell a part, 103 by 69 pixels, c581890205440a60d7a8634fe8ed6372d73e2af202d0e83ed7cf615640102e72
wrote assets/frames/folk-parts.png at 4 times, 412 by 276
assets/frames/folk-parts.png: the eighteen parts at four pixels to
one, in the seven entries they are drawn in. Reading down: four heads, four pairs
of eyes, four mouths, six hairs. Every head keeps its eye line on row 7 and its
mouth line on row 10, so any cell of the second row and any cell of the third
lands on skin on any cell of the first.
Two pixels between a line and a smile
The bar every asset in this book is held to was set when the walker was drawn, and it is one question: would you take a screenshot of this world without being asked to. At sixteen pixels a face clears it or does not on a handful of decisions, and the decisions are countable. The bench counts them. For each kind of part it takes every pair of cells in the row, counts the pixels the two differ in, and reports the closest pair, at the size the parts were drawn and at a smaller size the parts are sampled to the way the creature sheet was sampled to ten.
$ go run ./cmd/mkfolk -mode small
mkfolk: the parts at 16 pixels, the size they were drawn, and sampled to 12
kind at 16 closest pair, and by how much at 12 closest pair, and by how much
heads 16 round and square 3 round and square
eyes 4 open and aside 0 open and aside
mouths 2 line and smile 0 line and frown
hair 11 crop and bun 6 crop and swept
the four pairs of eyes at 16, rows 5 to 8, one character a pixel, and the four mouths, rows 8 to 12
5 ....................................................................
6 ......................................eee..eee.........ooo..ooo.....
7 ....eoe..eoe.........oee..oee.........eoe..eoe.........eoe..eoe.....
8 ....................................................................
8 ....................................................................
9 ......................o....o........................................
10 ......oooo.............oooo.............oooo............oeeeeo......
11 .......................................o....o.......................
12 ....................................................................
the eyes sampled to 12, rows 3 to 5: the sampler reads source row y*16/12 and never reads rows [3 7 11 15]
3 ....................................................
4 ....................................................
5 .............................eee.ee.......ooo.oo....
At sixteen, the smallest difference the library carries is two pixels: the line and the smile differ by the two raised corners on row 9, and the four pairs of eyes are never closer than four. On a screen that draws a frame pixel four pixels wide, which is how volume 2 opened the window, two pixels of difference is a mark eight screen pixels long, and the smile reads. At one pixel to one, the size of a full stop on this page, what reads is the hair and the colouring; the eyes are two dots and the mouth is a dash, and that is what the inspector under figure 99.1 is for.
Sampled to twelve, the table says what twelve costs, and it costs rows. The sampler reads source row y × 16 / 12 for each of
its twelve rows, which reaches rows 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13 and 14 and
never rows 3, 7, 11 or 15. Row 7 is the eye line. Two of the four pairs of eyes are
drawn on row 7 alone, so at twelve they are not there, and the closest pair of
eyes differs by nought; the line and the frown differ on row 11 alone, so they are
the same mouth. A hand-drawn twelve would place its pixels better than a sampler
does, and it would still have one pixel for an eye and no room for a white on
either side of it. Sixteen is the size at which an eye can be three pixels, and
that is the whole of the argument for it.
Two more decisions were made by a program rather than by looking, and both are in
cmd/mkfolk/art_test.go. The first composes every hair over every pair of
eyes and every mouth on every head, 384 faces, and asks that every pixel the eyes and
the mouth drew is still theirs afterwards. The long hair's first draft had its
sides at columns 3 and 4, and column 4 is the outer white of the left eye, so every
long-haired face would have had two-pixel eyes; the test is what stops the next hair
doing the same. The second asks that every head has skin under every pixel an eye or a mouth
can occupy, on rows 6, 7, 9, 10 and 11, and the narrow head failed it: its jaw came
in a column at row 11, the frown's two dropped corners landed on the outline there,
ink on ink, and a narrow-headed villager could not frown. Both parts were redrawn,
and both tests are the reason a fifth head or a seventh hair cannot quietly bring
either fault back.
$ go test -count=1 ./cmd/mkfolk/ -run 'TestEveryPartIsSixteenSquareAndDrawnInTheStrip|TestEveryHeadHasSkinUnderTheEyesAndTheMouth|TestNoHairCoversAnEyeOrAMouth' -v
=== RUN TestEveryPartIsSixteenSquareAndDrawnInTheStrip
--- PASS: TestEveryPartIsSixteenSquareAndDrawnInTheStrip (0.00s)
=== RUN TestEveryHeadHasSkinUnderTheEyesAndTheMouth
--- PASS: TestEveryHeadHasSkinUnderTheEyesAndTheMouth (0.00s)
=== RUN TestNoHairCoversAnEyeOrAMouth
--- PASS: TestNoHairCoversAnEyeOrAMouth (0.00s)
PASS
ok theworld/cmd/mkfolk 0.006s
The first of the three is paint's three refusals as a test, so a
character outside the strip is caught before the program that would refuse it is
run. The other two are the drawing rules above, held where a rule about pixels
belongs: beside the pixels, in the directory that holds the table.
Seven turns of times thirty-one, and a number under 3072
Halla's row has to become one of the sheet's faces, and the sheet can make 4 × 4 × 4 × 6 × 8 of them, which is 3072. So the job is to turn a row into a number under 3072, and then to read the number as five digits: which head, which eyes, which mouth, which hair, which colouring. The second half is a mixed radix, and the book has done it twice already: a water cell's four neighbours became one number under sixteen in chapter 18, and a byte became a column and a row of the font sheet in chapter 21, both by dividing and keeping the remainder. The first half is a hash, and it is the oldest one there is.
Start with nought. For each value in turn, multiply what you have by 31, add the value, and keep the remainder on division by 65521. The values are the home column, then the home row, then the name a letter at a time, each letter as its byte: H is 72, a is 97, l is 108. Halla lives on 8,5.
0 × 31 + 8 = 8. Then 8 × 31 + 5 = 253. Then 253 × 31 + 72 = 7915. Then 7915 × 31 + 97 = 245462, and 245462 is 3 × 65521 + 48899, so the key is 48899. Then 48899 × 31 + 108 = 1515977, which is 23 × 65521 + 8994. Then 8994 × 31 + 108 = 278922, which is 4 × 65521 + 16838. Then 16838 × 31 + 97 = 522075, which is 7 × 65521 + 63428. Seven turns, one for each value, and the key is 63428.
63428 is 20 × 3072 + 1988, so Halla's face is number 1988. Now peel it, lowest digit first, each radix in the order the parts are laid down. 1988 divided by 4 is 497 remainder 0: head 0, the round one. 497 divided by 4 is 124 remainder 1: eyes 1, aside. 124 divided by 4 is 31 remainder 0: mouth 0, the line. 31 divided by 6 is 5 remainder 1: hair 1, long. 5 divided by 8 is 0 remainder 5: colouring 5. Run it back the other way and it is the same number: 5 × 6 is 30, plus 1 is 31; 31 × 4 is 124, plus 0; 124 × 4 is 496, plus 1 is 497; 497 × 4 is 1988, plus 0. Every number under 3072 is exactly one face and every face is exactly one number, which is what makes the radix a base and not a lookup.
Why 31, and why 65521. The multiplier is prime and small enough to multiply by hand, which is all that is asked of it; 31 is the value a great many string hashes have used since the nineties, and nothing else about it matters. The modulus does matter, and the shipped value was the second one tried. Taking the remainder on 3072 directly, so that the key was already a face number, looked simpler, and under it the fold has a habit: 2774 × 31 + 108 leaves 86 on division by 3072, and 86 × 31 + 108 leaves 2774 again, so two letters l in a row undo themselves and "Ha" and "Hall" are the same key. That is not one unlucky pair. The keys that return to themselves after "ll" under a modulus m are the solutions of 960 × k = −3456 modulo m, and 960 and 3072 share a factor of 192, so there are 192 of them: one key in sixteen. Under a prime there is one solution, one key in 65521. 65521 is the largest prime under 65536, the same modulus Adler-32 uses, and it keeps every intermediate value under seventy thousand.
Extend internal/village/face.go. Steps returns every turn
so a bench can print the derivation and a reader can check it line by line;
Key is its last value; FaceOf is the whole derivation, a row
in and a face out, with no state anywhere; and Index is the peel run
backwards, which a test walks for all 3072 numbers.
// internal/village/face.go — add below the strip's positions
// Face is which cell of each row a villager is drawn from, and which
// colouring the cells are put through. It is worked out from a Person
// by FaceOf and is never written back.
type Face struct {
Head, Eyes, Mouth, Hair, Tint int
}
// The fold. A key is built from the row's fields by the oldest string
// hash there is: multiply what you have by Mult, add the next value,
// keep the remainder on division by Mod. The multiplier is small enough
// to work by hand and the modulus is the largest prime under 65536,
// prime because a multiplier and a modulus that share factors cycle:
// folded with the remainder taken on Faces instead, the letters "Ha"
// and "Hall" come to the same number.
const (
Mult = 31
Mod = 65521
)
// Step is one turn of the fold, kept so a bench can print the whole
// derivation and a reader can check it line by line.
type Step struct {
What string // the field, or the letter
Value int // what was folded in
Key int // the key after it
}
// fold is one turn: what there was times Mult, plus the value, mod Mod.
func fold(key, value, mod int) int {
return (key*Mult + value) % mod
}
// Steps folds the two fields of the roll that make a person this
// person and not another, in a fixed order: the home column, the home
// row, then the name a byte at a time. The order is the whole of what
// makes the face total: the same row folded in the same order is the
// same number, and a field folded in a different place is a different
// number.
//
// The home goes in first. Whatever is folded last moves the key by
// exactly its own difference, so two rows alike in everything but that
// field differ by one, and one is the head alone. Neighbours a row
// apart are common in a village; names alike in every letter but the
// last are not.
//
// The persona is prose and stays out: it is read by people, not by
// arithmetic. The law profile stays out because a face that changed
// with it would be a law you can see, and law in this world is a file.
// Carrying stays out because it is the one number an operator is meant
// to change. The founding tick stays out because a village founded a
// tick later is the same village.
func Steps(p Person, mod int) []Step {
var out []Step
key := 0
key = fold(key, p.Home.X, mod)
out = append(out, Step{"home column", p.Home.X, key})
key = fold(key, p.Home.Y, mod)
out = append(out, Step{"home row", p.Home.Y, key})
for i := 0; i < len(p.Name); i++ {
key = fold(key, int(p.Name[i]), mod)
out = append(out, Step{string(p.Name[i]), int(p.Name[i]), key})
}
return out
}
// Key is the fold's last value: one number under Mod for this row.
func Key(p Person) int {
steps := Steps(p, Mod)
return steps[len(steps)-1].Key
}
// FaceOf is the whole derivation: a row in, a face out, no state
// anywhere. The key is taken mod Faces, so that it names one of the
// faces the sheet can make, and then peeled.
func FaceOf(p Person) Face {
return Peel(Key(p) % Faces)
}
// Peel writes a number under Faces in the sheet's mixed radix: the
// head is the remainder on division by Heads, the eyes the remainder of
// what is left on division by Eyes, and so on up to the colouring.
// Every number under Faces is exactly one face and every face is
// exactly one number.
func Peel(n int) Face {
var d [5]int
for i, r := range Radix {
d[i] = n % r
n /= r
}
return Face{Head: d[0], Eyes: d[1], Mouth: d[2], Hair: d[3], Tint: d[4]}
}
// Index is the peel run backwards: the one number under Faces this
// face is.
func (f Face) Index() int {
d := [5]int{f.Head, f.Eyes, f.Mouth, f.Hair, f.Tint}
n := 0
for i := len(Radix) - 1; i >= 0; i-- {
n = n*Radix[i] + d[i]
}
return n
}
$ go run ./cmd/mkfolk -mode key
mkfolk: the fold for Halla, home 8,5: times 31, plus the value, mod 65521
folded in value key
home column 8 8
home row 5 253
H 72 7915
a 97 48899
l 108 8994
l 108 16838
a 97 63428
the key is 63428, and 63428 mod 3072 is 1988: face 1988 of the 3072 the sheet can make
peeled, lowest digit first
1988 mod 4 = 0 head round
497 mod 4 = 1 eyes aside
124 mod 4 = 0 mouth line
31 mod 6 = 1 hair long
5 mod 8 = 5 colouring light skin, brown hair
and 1988 is 1988 back again: round head, aside eyes, line mouth, long hair, light skin, brown hair
keys under 65521 that come back to themselves after "ll": 1 of 65521
The seven lines of the table are the seven turns of the interlude, and the peel under it is the interlude's second paragraph, so the two can be checked against each other line by line; the bench prints the modulus and the multiplier it used off the package's constants, and the last line counts the "ll" cycle under that modulus by trying every key. What the table shows about the fold's order is on its last line: the letter folded last adds itself to the key, so a row that differed from Halla's in its final letter alone would have a key one higher, and one higher is the next head. Exercise 2 runs that row. The home goes in first for exactly that reason: neighbours a row apart, which is what Halla and Mose are, get their one row of difference multiplied by 31 once for every letter that follows it, and neighbours are common in a village where a last letter's difference is not.
$ go run ./cmd/mkfolk -mode key -mod 3072
mkfolk: the fold for Halla, home 8,5: times 31, plus the value, mod 3072
(the shipped modulus is 65521; this run is under 3072 off the flag)
folded in value key
home column 8 8
home row 5 253
H 72 1771
a 97 2774
l 108 86
l 108 2774
a 97 75
the key is 75, and 75 mod 3072 is 75: face 75 of the 3072 the sheet can make
peeled, lowest digit first
75 mod 4 = 3 head narrow
18 mod 4 = 2 eyes wide
4 mod 4 = 0 mouth line
1 mod 6 = 1 hair long
0 mod 8 = 0 colouring dark skin, black hair
and 75 is 75 back again: narrow head, wide eyes, line mouth, long hair, dark skin, black hair
keys under 3072 that come back to themselves after "ll": 192 of 3072
The same seven values under the modulus that was tried first. The fourth and sixth lines are the cycle: after "a" the key is 2774, after "ll" it is 2774 again, and the last line counts a hundred and ninety-two keys under 3072 with that habit against one under 65521. Nothing else moved: under 3072 Halla is face 75, a narrow head with wide eyes and black hair, and there is nothing wrong with that face. What is wrong with the modulus is that one key in sixteen forgets two letters under it, and a fold that forgets letters is a fold whose collisions come in families instead of by chance.
Eight colourings on one strip
A face number's last digit is a colouring, and a colouring is a substitution on the strip: the three skin entries come out as one ramp and the two hair entries as one pair, and the outline and the white come out as themselves. Two skins is what sixteen colours buy, the soil ramp whole and the soil ramp moved one step toward bone, and five hairs: the rock ramp's shadow and midtone, which is how the sheet is drawn and reads as black; its midtone and lit, grey; the soil ramp's shadow and midtone, brown; soil's midtone under gold, blond; and soil's shadow under ember, red. Ten pairs of a skin and a hair, and eight of them ship.
The two that do not are the ones where the hair has no edge. Brown hair on dark skin puts the hair's lit crown, soil's midtone, on a brow that is soil's midtone, and enlarged the hair and the face were one brown lump with a darker rim; blond on dark skin puts its cap, soil's midtone again, on the same brow, which is the same pixel for the same reason. The rule that fell out of looking is that no entry of a hair may be the skin's midtone, and a test holds it, so that a ninth colouring added in a hurry cannot merge. The rule is a fact about this palette and these parts: hair meets skin along the brow with no ink between them, because an outline there reads as a hat, and where two materials meet with no line the tones have to do the separating.
Extend internal/village/face.go. A colouring names five entries of the
sixteen; Table is the substitution it makes on the seven, and
Remap is that table as the pair of colour lists the blit takes, which is
chapter 68's Look.Remap over a shorter list.
// internal/village/face.go — add below the strip's positions
// Tint is a colouring: which three entries of the sixteen the skin's
// shadow, midtone and lit come out as, and which two the hair's dark
// and lit come out as. Each is an index into gene.Sixteen.
type Tint struct {
Name string
Skin [3]int
Hair [2]int
}
// The two skins and the five hairs the colourings are built from. Two
// skins is what sixteen colours buy: the soil ramp whole, and the soil
// ramp moved one step toward bone.
var (
dark = [3]int{gene.SoilD, gene.SoilM, gene.SoilL}
light = [3]int{gene.SoilM, gene.SoilL, gene.Bone}
black = [2]int{gene.RockD, gene.RockM}
grey = [2]int{gene.RockM, gene.RockL}
brown = [2]int{gene.SoilD, gene.SoilM}
blond = [2]int{gene.SoilM, gene.Gold}
red = [2]int{gene.SoilD, gene.Ember}
)
// Colourings are the eight the sheet may come out in. Not every pair
// of a skin and a hair is here: a hair whose entries include the skin's
// midtone has no edge against the brow, and brown hair on dark skin is
// exactly that pair, so it is left out and a test holds the rule.
var Colourings = [Tints]Tint{
{"dark skin, black hair", dark, black},
{"dark skin, grey hair", dark, grey},
{"dark skin, red hair", dark, red},
{"light skin, black hair", light, black},
{"light skin, grey hair", light, grey},
{"light skin, brown hair", light, brown},
{"light skin, blond hair", light, blond},
{"light skin, red hair", light, red},
}
// ...
// Table is the substitution this face makes on the strip: entry i of
// the seven comes out as entry Table()[i] of the sixteen. The outline
// and the white are fixed points; the skin's three and the hair's two
// come out as the colouring says.
func (f Face) Table() [7]int {
t := Strip
c := Colourings[f.Tint]
t[SkinD], t[SkinM], t[SkinL] = c.Skin[0], c.Skin[1], c.Skin[2]
t[HairD], t[HairL] = c.Hair[0], c.Hair[1]
return t
}
// Remap is that table as the pair of colour lists the blit takes.
func (f Face) Remap() render.Remap {
from := make([]render.Color, len(Strip))
to := make([]render.Color, len(Strip))
for i, j := range Strip {
from[i] = gene.Sixteen[j]
}
for i, j := range f.Table() {
to[i] = gene.Sixteen[j]
}
return render.Remap{From: from, To: to}
}
// Cells are the four source rectangles of the sheet this face is
// drawn from, in the order they are laid down.
func (f Face) Cells(s *render.Sheet) [Rows]render.Rect {
return [Rows]render.Rect{
s.Frame(f.Head, HeadRow),
s.Frame(f.Eyes, EyesRow),
s.Frame(f.Mouth, MouthRow),
s.Frame(f.Hair, HairRow),
}
}
// Draw lays the four cells over one another at (x, y) on the buffer,
// each put through the face's substitution on the way. A pixel a part
// does not hold leaves what is under it alone, which is how the eyes
// sit on the head and the hair sits on both.
func (f Face) Draw(b *render.Buffer, s *render.Sheet, x, y int) {
m := f.Remap()
for _, r := range f.Cells(s) {
b.BlitMap(s, r, x, y, Cell, Cell, &m)
}
}
$ go run ./cmd/mkfolk -mode tints
mkfolk: the 8 colourings, as substitutions on the strip's seven entries
# colouring skin: shadow mid lit hair: dark lit
0 dark skin, black hair 4C2F22 75563B A1875A 323847 5A6470
1 dark skin, grey hair 4C2F22 75563B A1875A 5A6470 8A979E
2 dark skin, red hair 4C2F22 75563B A1875A 4C2F22 C75938
3 light skin, black hair 75563B A1875A F5EBD7 323847 5A6470
4 light skin, grey hair 75563B A1875A F5EBD7 5A6470 8A979E
5 light skin, brown hair 75563B A1875A F5EBD7 4C2F22 75563B
6 light skin, blond hair 75563B A1875A F5EBD7 75563B F0BE35
7 light skin, red hair 75563B A1875A F5EBD7 4C2F22 C75938
every colouring keeps the outline at entry 0 and the white at entry 15
Halla's face as the sheet holds it, one character a pixel, each character a strip entry
................
.....oooooo.....
....oHHHHhho....
..oHHHHhhhhhho..
..oHHkkkkkkhho..
..oHhkkkkkkhho..
..oHkkkkkkkkho..
..oHoeekkoeeho..
..oHkkkkskksho..
..ohlkkkskksho..
..ohkkooooksho..
..ohokkkkssoho..
..ohooooooooho..
..ooo.osso.ooo..
......okko......
......oooo......
entry comes out what it is on the sheet
0 0 outline 181A29 -> 181A29
1 2 skin shadow 4C2F22 -> 75563B
2 3 skin midtone 75563B -> A1875A
3 15 skin lit A1875A -> F5EBD7
4 1 hair dark 323847 -> 4C2F22
5 2 hair lit 5A6470 -> 75563B
15 15 white F5EBD7 -> F5EBD7
the same face through colouring 5, light skin, brown hair, each character an entry of the sixteen
................
.....000000.....
....02222110....
..022221111110..
..022333333110..
..021333333110..
..023333333310..
..020ff330ff10..
..023333233210..
..01f333233210..
..013300003210..
..010333322010..
..010000000010..
..000.0220.000..
......0330......
......0000......
Draw is four calls to the blit chapter 68 wrote, at the cell's own size,
through one substitution, and nothing in render changed for it: a
composed face is a sheet cell blitted four times with the alpha channel doing the
layering, which is what the alpha channel has been for since the sprite chapter.
The two pictures of Halla are the same picture, and every character in the second
is the first's put through the table above it: k became 3
where the table says entry 2 comes out as entry 3, h became
1, and the o of every outline and the e of each
eye's white came out as themselves. The face is composed on the sheet's own
colours and substituted on the way to the buffer, inside the blit, for the reason
chapter 68's worked failure gave: once the pixels are down they are colours with no
history, and a substitution run over a finished picture recolours whatever else
happens to share an entry with a face.
The claim this page makes is that the same row always makes the same face, and the bench makes it as something that can fail: it reads the roll off the embedded file, composes a face for everyone in it, hashes the pixels, and then reads the roll again and does it all a second time.
$ go run ./cmd/mkfolk -mode faces
mkfolk: assets/sprites/folk.png, 96x64, 24 cells of 16
parts chosen off each row: home column, home row, name, folded times 31 mod 65521
pass 1: the roll read from the file, 3 people
name home key face head, eyes, mouth, hair, colouring
Halla 8,5 63428 1988 0 1 0 1 5 round head, aside eyes, line mouth, long hair, light skin, brown hair
pixels sha256 c1b27d2f5782d9d2
Ander 9,5 27844 196 0 1 0 3 0 round head, aside eyes, line mouth, fringe hair, dark skin, black hair
pixels sha256 e48fca05e27fe03b
Mose 8,6 54642 2418 2 0 3 1 6 square head, open eyes, grin mouth, long hair, light skin, blond hair
pixels sha256 db7735226cb801c9
pass 2: the roll read from the file, 3 people
name home key face head, eyes, mouth, hair, colouring
Halla 8,5 63428 1988 0 1 0 1 5 round head, aside eyes, line mouth, long hair, light skin, brown hair
pixels sha256 c1b27d2f5782d9d2
Ander 9,5 27844 196 0 1 0 3 0 round head, aside eyes, line mouth, fringe hair, dark skin, black hair
pixels sha256 e48fca05e27fe03b
Mose 8,6 54642 2418 2 0 3 1 6 square head, open eyes, grin mouth, long hair, light skin, blond hair
pixels sha256 db7735226cb801c9
the second pass agrees with the first for 3 of 3 faces
Three keys, three face numbers, three sets of five digits and three hashes, twice, and the second pass agrees with the first for all three. Halla is 1988, worked by hand above. Ander's key is 27844, which is 9 × 3072 + 196: round head, aside eyes, line mouth, fringe, dark skin and black hair. Mose's is 54642, which is 17 × 3072 + 2418: square head, open eyes, grin, long hair, light skin and blond hair. Halla and Ander share a head, a pair of eyes and a mouth, because 1988 and 196 leave the same remainder on division by 64, and what tells them apart is the hair and the colouring; at a glance that is what tells people apart anyway, and the test that pins this run refuses only two rows that come out as one face.
$ go run ./cmd/mkfolk -mode card -shot assets/frames/folk-three.png
mkfolk: the 3 people of the roll, composed off their rows
Halla face 1988 round head, aside eyes, line mouth, long hair, light skin, brown hair
Ander face 196 round head, aside eyes, line mouth, fringe hair, dark skin, black hair
Mose face 2418 square head, open eyes, grin mouth, long hair, light skin, blond hair
the card 108 by 37 pixels, 285ceee38555cf3d1db6b9600c3dd70380c8ac013c1a1b368ec1becc13ee8dc1
wrote assets/frames/folk-three.png at 4 times, 432 by 148
assets/frames/folk-three.png: Halla, Ander and Mose, composed off
their rows, at four pixels to one. Every pixel of all three is one of the valley's
sixteen, and every part on every face is a cell of figure 99.1 put through one of
the eight tables.
$ go test -count=1 ./internal/village/ -run 'TestTheSameRowMakesTheSameFace|TestEveryNumberUnderFacesIsOneFaceAndBack|TestNoColouringPutsTheHairOnTheSkinsMidtone' -v
=== RUN TestTheSameRowMakesTheSameFace
--- PASS: TestTheSameRowMakesTheSameFace (0.00s)
=== RUN TestEveryNumberUnderFacesIsOneFaceAndBack
--- PASS: TestEveryNumberUnderFacesIsOneFaceAndBack (0.00s)
=== RUN TestNoColouringPutsTheHairOnTheSkinsMidtone
--- PASS: TestNoColouringPutsTheHairOnTheSkinsMidtone (0.00s)
PASS
ok theworld/internal/village 0.005s
The first reads the roll twice and asks for the same key and the same face for
everyone; then it moves each person one row south and adds a letter to each name
and asks for a different key both times; then it blanks the persona, changes the
law to bandit, empties the grams and moves the founding tick, and asks that the
face did not move. The second walks all 3072 numbers through Peel and
Index and asks that each comes back as itself and that no two peel to
one face. The third is the colouring rule from above, and it also asks that every
entry a colouring names is one of the sixteen and that the outline and the white
are fixed points of every table.
The fold is seven lines of arithmetic, and a reader who has met a random stream knows a shorter way: draw five numbers off a stream and read them as the five digits. It costs nothing, every face on the sheet is reachable, and the bench does it off a flag. The stream is seeded, as every stream in this book is, so the run below is exact; the seed is on the flag, because there is nowhere else for it to be.
$ go run ./cmd/mkfolk -mode card -random -seed 1
mkfolk: the 3 people of the roll, parts chosen at random off seed 1, which is nothing on any row
Halla face 1579 narrow head, wide eyes, frown mouth, crop hair, light skin, grey hair
Ander face 1608 round head, wide eyes, line mouth, long hair, light skin, grey hair
Mose face 1163 narrow head, wide eyes, line mouth, crop hair, light skin, black hair
the card 108 by 37 pixels, eba92a190c8bb8deb14ee4ee17268b496dd41fe8772fd43a2e784ea968f15c16
$ go run ./cmd/mkfolk -mode card -random -seed 2
mkfolk: the 3 people of the roll, parts chosen at random off seed 2, which is nothing on any row
Halla face 875 narrow head, wide eyes, frown mouth, long hair, dark skin, red hair
Ander face 2240 round head, open eyes, line mouth, bare hair, light skin, brown hair
Mose face 2009 long head, wide eyes, smile mouth, long hair, light skin, brown hair
the card 108 by 37 pixels, 03fbe61344a4d310ac6f52c8bb197e54379d5f5503e552aabd6f6efe2cb9f209
Two runs, the same file, and Halla has two faces: a grey-haired crop with a frown
under seed 1, and long red hair under seed 2, on a narrow head both times by luck.
Start from the symptom and work back. The row did not change between the runs, so
nothing that changed came from the row; what changed was the seed, and the seed is
not on any row, it is on the command line, which means the face belongs to whoever
started the program and not to Halla. Under one seed the run replays, and that is
the trap: -mode faces -random -seed 1 reads the roll twice and agrees
with itself three of three, exactly as the fold does, so a test that reads the roll
twice under one seed cannot tell the two designs apart. Look at Ander instead. His
digits come off the stream after Halla's, so his face depends on the order the file lists the people in and on
Halla existing at all; put Mose first in the file and everyone's face moves, and
found a fourth villager and the fifth's face moves. A face drawn off a stream is a
fact about the run, and a face folded off a row is a fact about the row. The fix is
the rest of this page.
Why faces come from row numbers
The mechanism underneath this chapter is the one under chapter 18's tileset and chapter 21's font, and it is the same move each time: several small choices are one number, and a number can be stored, compared, printed and taken apart without anybody agreeing on what its digits mean beyond the radix. Four neighbours were a mask under sixteen; a byte was a column and a row under sixteen columns; five parts are a face under 3072, with a radix a digit. What this chapter adds is the front half: the number does not have to be given, it can be made, and a hash is nothing more than a way of making one number out of several values in a fixed order so that the same values always make the same number. Every property the page argued for falls out of that order being fixed. A row read twice is the same number. A row with one field changed is a different number. A row nobody wrote down, a stream, a clock, a seed on a flag, has no number to make.
The second thing underneath is chapter 68's, unchanged: a phenotype is computed, and
a computed thing cannot get out of step with what it is computed from. There is no
face field on Person for a line of simulation code to assign to, no
portrait file for an editor to leave stale, and no cache to invalidate when the roll
changes; a headless run through two centuries of ticks never calls
FaceOf once, and a run that draws the village calls it every time it
draws, for a few dozen multiplications a face. The derivation lives in
village because a question about what a row means belongs with the row,
and the blit lives in render, which gained nothing on this page: a face is
four cells of a sheet through one substitution, and the renderer already knew how to
do that for an animal.
What the design costs is stated on the page rather than hidden in it. Two rows one letter apart in the last letter are two faces one head apart, because the fold's last turn adds the letter; the home goes in first so that the common case, two neighbours, is not the cheap one. A roll of twenty people has sixty-four ways to combine a head, eyes and a mouth, and twenty draws from sixty-four nearly always share one, as two of these three already do, and the sheet's answer to that is more parts, not a better hash: a fifth head is a row of the table and one constant, and the tests here already refuse a fifth head with a jaw too narrow for the frown or a seventh hair that covers an eye. And a face that is arithmetic is a face an operator can change only by changing the row, which is the point and is also a limit: Halla cannot be given a different face without being given a different name or a different home, because there is nothing else about her the face is made of.
- Fold a villager's home column, home row and name by hand, seven turns of times
31 plus the value mod 65521, and check the key against
-mode keyturn by turn. - Peel a number under 3072 into a head, a pair of eyes, a mouth, a hair and a colouring, lowest digit first, and run it back to the number.
- Say why the home is folded before the name, what a change in the last letter alone does to the key, and which digit that moves.
- Name the seven entries the sheet is drawn in, say which two are fixed points of every colouring, and say why brown hair on dark skin is not one of the eight.
- Say which source rows the sampler skips at twelve, why the eye line is one of them, and what that does to the closest pair of eyes and the closest pair of mouths.
- Run the random flag under two seeds, say what changes between the runs and what does not, and say why Ander's face under a seed depends on Halla's.
Exercise 1 — Mose, on paper. Mose lives on 8,6. Fold his
row by hand, six turns, and peel the result, before running
-mode key -who Mose.
0 × 31 + 8 = 8; 8 × 31 + 6 = 254; 254 × 31 + 77 = 7951; 7951 × 31 + 111 = 246592, which is 3 × 65521 + 50029; 50029 × 31 + 115 = 1551014, which is 23 × 65521 + 44031; 44031 × 31 + 101 = 1365062, which is 20 × 65521 + 54642. Then 54642 is 17 × 3072 + 2418, and 2418 peels to 2, 0, 3, 1, 6: a square head, open eyes, the grin, long hair, and light skin under blond hair. The second turn is the only one that differs from Halla's, 254 against 253, and by the third turn the two keys are 36 apart and never near each other again.
$ go run ./cmd/mkfolk -mode key -who Mose
mkfolk: the fold for Mose, home 8,6: times 31, plus the value, mod 65521
folded in value key
home column 8 8
home row 6 254
M 77 7951
o 111 50029
s 115 44031
e 101 54642
the key is 54642, and 54642 mod 3072 is 2418: face 2418 of the 3072 the sheet can make
peeled, lowest digit first
2418 mod 4 = 2 head square
604 mod 4 = 0 eyes open
151 mod 4 = 3 mouth grin
37 mod 6 = 1 hair long
6 mod 8 = 6 colouring light skin, blond hair
and 2418 is 2418 back again: square head, open eyes, grin mouth, long hair, light skin, blond hair
keys under 65521 that come back to themselves after "ll": 1 of 65521
Exercise 2 — one letter away. The key mode takes a name and
a home that are in no roll. Before running -mode key -name Hallb -home
8,5, say what the key will be and which of Halla's five digits will
move.
Every turn but the last is Halla's, because the first six values are the same;
the last adds 98 where Halla's added 97, so the key is 63429 and the face is
1989. One more, lowest digit first, is the head: 1989 mod 4 is 1, the long head,
and the other four digits are Halla's. The test in face_test.go adds a
letter after the last one instead, which is one more turn of the fold, and there
the whole key moves.
$ go run ./cmd/mkfolk -mode key -name Hallb -home 8,5
mkfolk: the fold for Hallb, home 8,5: times 31, plus the value, mod 65521
folded in value key
home column 8 8
home row 5 253
H 72 7915
a 97 48899
l 108 8994
l 108 16838
b 98 63429
the key is 63429, and 63429 mod 3072 is 1989: face 1989 of the 3072 the sheet can make
peeled, lowest digit first
1989 mod 4 = 1 head long
497 mod 4 = 1 eyes aside
124 mod 4 = 0 mouth line
31 mod 6 = 1 hair long
5 mod 8 = 5 colouring light skin, brown hair
and 1989 is 1989 back again: long head, aside eyes, line mouth, long hair, light skin, brown hair
keys under 65521 that come back to themselves after "ll": 1 of 65521
Exercise 3 — twenty-four, by the same sampler. Run
-mode small -at 24. Say before you run it what the closest pairs will
be, and then explain why the eyes at twenty-four are not all the same
width.
Nothing collapses going up: every source row is read at least once, so no two parts become one. The heads, the eyes and the mouths keep their closest pairs, with the differences grown by the sampling to 32, 6 and 3; the hair's closest pair changes, from the crop and the bun at eleven to the crop and the swept at twenty-four, because the columns the sampler reads twice fall on the two pairs differently. What twenty-four adds is unevenness. Twenty-four over sixteen is one and a half, and a pixel cannot be sampled one and a half times, so alternate source columns are read twice and once, and a three-pixel eye comes out five wide on the left and four wide on the right. An enlargement that is a whole number reads every source pixel the same number of times, which is why the figures on this page are at four and the inspector offers one, two and four.
$ go run ./cmd/mkfolk -mode small -at 24 | tail -7
the eyes sampled to 24, rows 7 to 12: the sampler reads source row y*16/24 and never reads rows []
7 ....................................................................................................
8 ....................................................................................................
9 ........................................................eeeee...eeee.............ooooo...oooo.......
10 ........................................................eeeee...eeee.............ooooo...oooo.......
11 ......eeoee...eooe.............ooeee...oeee.............eeoee...eooe.............eeoee...eooe.......
12 ....................................................................................................
Three people have faces, and each face is a number a reader can work out from the roll with a pencil and check against a sheet the program rebuilds on every run. A face is sixteen pixels and Halla's name is thirty pixels of six-by-eight glyphs; what neither has is a place to be said from, and a line the hero model writes, wrapped to a width and set beside a face in a frame that stretches to fit it, is a picture the renderer has never drawn. It has a font, a blit that clips, and a text width it can measure; it does not have a box.