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

The Spring Feeds the Soil

The spring becomes a rate

The spring at (10, 1) has been turning soil into water since the first volume, and that law cannot feed a root. A root needs an amount it can spend down. Moisture is a store with a surface: the spring adds an amount each tick, the water top is ground height plus moisture, flow moves from higher top to lower top, soil soaks part of it in, and sun evaporates part back out.

The old water law changed a cell's kind forever. The terrarium needs a smaller fact: this much water is here now. That fact can fall, rise and move sideways while the terrain kind stays soil.

The height table from the fire and water sweep stays useful because it names the slope. What changes is the comparison. Water no longer asks whether two ground cells differ; it asks which water surface stands higher after the stored amount is added.

The moisture grid

Water on this page lives in two places and they are different kinds of object, on purpose. The moisture inside the ground is the bed the previous chapter built: plants draw against it, it holds at most 1.00 to a cell, and only soil can hold any, since a root cannot live in open water or rock. The film standing on top of the ground is new, it covers every cell that is not rock, and it is the part that moves. Moving is why it needs a grid of its own.

A sheet holds a State, a small whole number naming a kind, and no arithmetic in the world makes soil plus soil mean anything. The film needs a float per cell and the identical discipline around it: one grid every rule reads, a second grid every sweep writes, neither reachable from outside the package. That is the same twenty lines with a different number in them, and they go next to the sheet they copy.

▣ Build · stage 1 — the sheet's other half
// internal/cells/store.go

// Store is a sheet that holds an amount per cell instead of a kind.
// Everything a sheet promises, a store promises the same way: one grid
// every rule reads, a second grid every sweep writes, neither of them
// reachable from outside this package. What changes is the number. A
// State names what a cell is; an amount says how much of something is
// standing in it, and an amount can be added, taken and counted.
type Store struct {
	W, H int

	now  []float64
	next []float64
}

// NewStore allocates a w-by-h store with every cell holding nothing.
func NewStore(w, h int) *Store {
	return &Store{W: w, H: h, now: make([]float64, w*h), next: make([]float64, w*h)}
}

// In reports whether a coordinate names a cell of this store.
func (s *Store) In(x, y int) bool { return x >= 0 && x < s.W && y >= 0 && y < s.H }

// At is the amount in one cell as it stood when this sweep began. Past
// the edge it answers 0, and a store needs no equivalent of the
// sheet's Off to say so: 0 is a state like any other and had to be
// distinguished, where 0 of something is exactly what an absent cell
// holds.
func (s *Store) At(x, y int) float64 {
	if !s.In(x, y) {
		return 0
	}
	return s.now[y*s.W+x]
}

// Amount is one cell's law over quantities: given the store and a cell
// on it, how much that cell holds next. Like a Rule it may read
// anything on the store and may write nothing.
type Amount func(s *Store, x, y int) float64

// Sweep applies one law to every cell of the store, each cell read out
// of the grid the sweep began with and written into the other one, and
// swaps the two when the last cell has been written.
func (s *Store) Sweep(a Amount) {
	for y := 0; y < s.H; y++ {
		for x := 0; x < s.W; x++ {
			s.next[y*s.W+x] = a(s, x, y)
		}
	}
	s.now, s.next = s.next, s.now
}

// Apply changes every cell out of its own amount alone. Nothing here
// reads a neighbour, so there is no order for a law to be spoiled by
// and the second grid goes untouched. It stays safe to leave it stale
// only because Sweep writes every cell of it before any rule reads
// one; the thrifty sweep that did not is in sheet.go, with the run it
// ruins.
func (s *Store) Apply(f func(x, y int, v float64) float64) {
	for y := 0; y < s.H; y++ {
		for x := 0; x < s.W; x++ {
			i := y*s.W + x
			s.now[i] = f(x, y, s.now[i])
		}
	}
}

Apply is the half that is new. Four laws run on this page's tick and only one of them looks at a neighbour; the other three change a cell out of what that cell alone is holding. A law like that cannot be tripped by the order it walks the grid in, so it does not need the second buffer and does not pay for it. Keeping the two apart in the package is a way of stating which kind a law is at the moment you write it.

// internal/cells/store.go, the rest of the file

// Add puts an amount into one cell from outside any sweep, the way a
// spring puts water into the cell it rises in. It is the one way an
// amount enters a store that no law wrote, so every unit a run adds is
// added here and counted here.
func (s *Store) Add(x, y int, v float64) {
	if !s.In(x, y) {
		return
	}
	s.now[y*s.W+x] += v
}

// Total is everything the store is holding, added up in row order. The
// order is written down because adding the same numbers in another
// sequence gives another answer in the last few bits, and this total is
// one side of a ledger that has to balance.
func (s *Store) Total() float64 {
	t := 0.0
	for _, v := range s.now {
		t += v
	}
	return t
}

// Bits is every cell's amount as raw float64 bits, big-endian, in row
// order. Two runs that part in the seventeenth digit print identical
// tables, so the digest runs over the numbers themselves and never over
// the columns.
func (s *Store) Bits() []byte {
	out := make([]byte, 0, len(s.now)*8)
	var buf [8]byte
	for _, v := range s.now {
		binary.BigEndian.PutUint64(buf[:], math.Float64bits(v))
		out = append(out, buf[:]...)
	}
	return out
}

None of those three is a law, and that is why they sit outside the sweep: they are how a store is written to, added up and hashed between ticks rather than during one. The digest is over the bits and not the printed columns for the reason volume 3 hashed the bits of a position: two runs that part in the seventeenth digit draw identical maps. And notice what is missing beside the sheet, which has a Set that writes a state into both of its buffers at once. A store has nothing of the kind. A kind is assigned; an amount is added to and taken from, and anything that could assign one from outside would be a way of moving water past the ledger.

The bed needs three small additions before water can arrive in it, and one of them is the point of the chapter. It can already be drawn from; it cannot yet be put into. It can be asked what one cell offers a root; it cannot yet be asked what the whole valley is holding. And it cannot hash itself, which matters because a run is only replayable if the digest covers all of the world and not the convenient half.

▣ Build · stage 2 — a deposit, a total, and a bed that hashes
// internal/terra/bed.go

// Soak puts water into one cell of ground and reports how much of it
// the ground took: nothing at all where a root could not live, and
// never past a full cell.
func (b *Bed) Soak(c sim.Coord, water, cap float64) float64 {
	if !b.Rootable(c) || water <= 0 {
		return 0
	}
	s := &b.cells[b.index(c)]
	took := math.Min(water, math.Max(cap-s.Moisture, 0))
	s.Moisture += took
	return took
}

// Held is everything in the bed, added up in row order. The order is
// written down because adding the same numbers in another sequence
// gives another answer in the last few bits, and this total is one side
// of a ledger that has to balance.
func (b *Bed) Held() Cell {
	var t Cell
	for _, s := range b.cells {
		t.Moisture += s.Moisture
		t.Nutrient += s.Nutrient
	}
	return t
}

// Bits is every cell's two numbers as raw float64 bits, big-endian, in
// row order: the same digest volume 3 took over positions, pointed at
// the ground instead.
func (b *Bed) Bits() []byte { ... }
// internal/cells/chain.go

// AddBits folds a run of raw bytes into the chain. Not every part of a
// world lives in this package, and a history is only worth hashing if
// it covers all of it: anything that can write its own state as bytes
// can be folded into the same chain as a sheet and a store.
func (c *Chain) AddBits(p []byte) {
	sum := sha256.New()
	sum.Write(c.h[:])
	sum.Write(p)
	copy(c.h[:], sum.Sum(nil))
}

// AddStore folds one tick's store into the chain, over the raw bits of
// every amount rather than the printed columns.
func (c *Chain) AddStore(s *Store) { c.AddBits(s.Bits()) }

Soak is Draw with the sign turned round, and writing it that way settles two arguments at once. The bed already knows which cells a root can live in and how full each one is, so it is the right place to decide how much water the ground accepts; and because it reports what it took, the film can subtract that exact number instead of working the amount out a second time. One number, computed once, used at both ends.

Chain itself is neither new nor moved: the type, the Add that folds a sheet into it and the Sum that prints it are still in sheet.go, byte for byte as volume 3 left them. chain.go is a second file of the same package holding the two methods this page needed — legal Go, and a way of saying in the file name that they arrived later. The chain now folds two things per tick, the film and the bed, which is the whole state this page can change. A digest over half a world is a digest that goes on matching while the other half drifts.

Regen retires here. It stays in the file, with a second paragraph of three lines added to its comment saying what replaced it, because deleting a placeholder is how the next reader ends up reinventing it: Chapter 37 retires it. Once water arrives at a cell because it ran there, the refill is a fact about the ground and not a constant, and Soak below is what puts it back. Nothing in this chapter calls it again.

Then the ground itself. Chapter 34's table counts in steps: the pond floor is 0, the ground rises by one for every cell you cross to reach it, rock is above everything. Water is counted in units. Those are two measures, so the program says once, before the first tick, what one step of the valley's side is worth in water: two units, which is two full cells of soil. After that no code multiplies anything by a height again.

▣ Build · stage 3 — the top of the water, and what runs off it
// cmd/soak/main.go

const (
	Spring = 0.80  // what the spring adds to its own cell every tick
	Rise   = 2.00  // how much water it takes to fill one step of the valley's side
	Soak   = 0.05  // the most one cell of soil takes off the surface in a tick
	Cap    = 1.00  // how much water a cell of soil holds when it is full
	Sun    = 0.02  // the sun's allowance: what it lifts off one wet cell in a tick
	Damp   = 0.001 // the least a cell can hold and still be called wet
	Pool   = 0.10  // the least standing water a printed map draws as a puddle
)

// level is the top of the water at one cell: the ground plus whatever
// is standing on it. Chapter 34 compared ground heights, because a
// cell was wet or dry and had no depth. A quantity has one, and the
// top of the water is what decides which way it goes.
func (v *valley) level(x, y int) float64 {
	return v.ground[y*Cols+x] + v.surf.At(x, y)
}

// spill is how much cell (x, y) hands to each of its four neighbours
// this tick. A cell hands its whole film to the ground below it, split
// between the ways down in proportion to how far each of them falls,
// and never enough down any one of them to put that neighbour above
// the cell it came from. This is the only place in the program that
// number is worked out, and both ends of every exchange call it.
func (v *valley) spill(x, y int) [4]float64 {
	var out, drop [4]float64
	have := v.surf.At(x, y)
	if have <= 0 || v.solid(x, y) {
		return out
	}
	top := v.level(x, y)
	fall, ways := 0.0, 0
	for i, d := range cells.Steps {
		nx, ny := x+d.DX, y+d.DY
		if v.solid(nx, ny) {
			continue
		}
		if f := top - v.level(nx, ny); f > 0 {
			drop[i] = f
			fall += f
			ways++
		}
	}
	if ways == 0 {
		return out
	}
	for i := range out {
		if drop[i] <= 0 {
			continue
		}
		share := have * drop[i] / fall
		even := drop[i] / float64(ways+1)
		out[i] = math.Min(share, even)
	}
	return out
}

Two clauses, and each does a job the other cannot. share is the runoff clause: water on a slope does not sit there, it goes, and the steepest way down takes the largest part of it. On its own that clause would slosh a puddle back and forth across a flat forever, since a cell sitting a hair above its neighbour would hand over everything it has and be handed most of it back. even stops that. Dividing the drop between the cell and the ways out of it gives the most that can move without putting the receiver above the giver, so a puddle spread over four level cells settles into four equal depths and stays. The smaller of the two clauses wins, every time.

Notice what the pond needed before it would behave like a pond: nothing. Its cells all sit at ground 0, so the only drop between them is the water itself and the levelling clause is the one that binds. The slope wets, the basin fills, and neither is a special case.

▣ Build · stage 4 — the law, written from the receiving end
// cmd/soak/main.go

// flow is the law: what one cell keeps, less what it hands to the
// neighbours below it, plus what the neighbours above it hand down.
// The steps are written in pairs, east then west and south then north,
// so flipping the low bit of an index turns a step into the step back:
// what the neighbour I reached going east hands to me is its own
// westward share.
func (v *valley) flow(s *cells.Store, x, y int) float64 {
	if v.solid(x, y) {
		return 0
	}
	w := s.At(x, y)
	for _, gift := range v.spill(x, y) {
		w -= gift
	}
	for i, d := range cells.Steps {
		nx, ny := x+d.DX, y+d.DY
		if v.solid(nx, ny) {
			continue
		}
		w += v.spill(nx, ny)[i^1]
	}
	return w
}

This is chapter 34's discipline holding under a heavier load. A rule decides one cell and writes one cell, its own, so a cell cannot push water into its neighbour; it can only ask the neighbour what it is sending. Every exchange is therefore computed twice, once by the giver working out what it hands over and once by the receiver working out what it is handed, and the two agree because they are the same call: spill on the giving cell, out of the grid nobody is writing to.

That claim is checkable and this is a chapter about checking things, so check it. Take a settled valley, pick one cell on the north-east slope, and print the four exchanges it is part of.

$ go run ./cmd/soak -mode hand -ticks 1200 -at 8,2
soak: cell (8,2) at tick 1200, and the four exchanges it is part of
      one step up the valley side is 2 units of water, so ground 3 is 6.00

  step   neighbour     ground     film        top       drop   hands over   takes back
  east   (9,2)         8.00   0.3357     8.3357    -2.1052     0.000000     0.167851
  west   (7,2)         4.00   0.1311     4.1311     2.0994     0.117375     0.000000
  south  (8,3)         4.00   0.2071     4.2071     2.0234     0.113124     0.000000
  north  (8,1)         8.00   0.1721     8.1721    -1.9417     0.000000     0.082648
  here   (8,2)         6.00   0.2305     6.2305

      0.230499 here, less 0.230499 handed away, plus 0.250499 handed in
      leaves 0.250499, and the rule returns 0.250499

      the whole grid this tick: 4.027525017 handed over, 4.027525017 taken in
      film before the sweep 35.925463791, after it 35.925463791, difference 7.105427357601002e-15

Read the drop column first, because every other number follows from its sign. Two of this cell's neighbours stand above it and two below, and the rule has nothing to say to the two above: a cell never reaches uphill, so both of those rows hand over 0.000000. What comes down from them is their business, worked out in their own spill, and it arrives as 0.167851 from the east and 0.082648 from the north. Meanwhile this cell empties itself completely, 0.117375 west and 0.113124 south, which together are its whole film to the last digit. It is a piece of channel. Everything that arrives leaves the same tick, and the film it carries is water in flight rather than water in store.

The last two lines are the ones to keep. Asked of all ninety-six cells at once, the water handed over and the water taken in are the same number to nine decimal places, and the film across the whole valley after the sweep is what it was before, short by 7.1 × 10−15. That is one step of the arithmetic at that size, the smallest amount by which two float64 values near 36 can differ, and it comes from adding ninety-six numbers in a different order, not from losing water. A sweep that moves water changes only where the water is.

The flow law and the books

Flow is one law of four, and the other three are what make the cycle a loop instead of a slide. The spring adds. The ground drinks what it can hold and gives it back slowly. The sun lifts a fixed allowance off every cell with anything to give, and that allowance is the only water in the program that goes somewhere the grid cannot see.

▣ Build · stage 5 — a tick, and the three numbers that audit it
// cmd/soak/main.go

// soak hands the film to the ground under it. The bed is the authority
// on how much it can take, so the amount is asked for once, the ground
// answers with what it kept, and the film gives up exactly that. Both
// ends of the transfer are the same number because only one of them
// ever worked it out.
func (v *valley) soak() {
	for y := 0; y < Rows; y++ {
		for x := 0; x < Cols; x++ {
			c := sim.Coord{X: x, Y: y}
			v.into[y*Cols+x] = v.bed.Soak(c, math.Min(Soak, v.surf.At(x, y)), Cap)
		}
	}
	v.surf.Apply(func(x, y int, w float64) float64 { return w - v.into[y*Cols+x] })
}

// sun takes the same allowance from every cell holding anything: off
// the film first, and out of the ground underneath for whatever the
// film could not cover. The second half is the bed's own Draw with a
// root of no radius at all, because evaporation takes water out of the
// ground exactly the way a root does and the clamp inside Draw is
// already the right clamp. This is the only place water leaves the
// valley, so it is the only place the gone column grows.
//
// It counts as it goes: how many cells it took anything from, and how
// many of those could pay the whole allowance. The interlude at the
// end of this chapter is about those two numbers.
func (v *valley) sun() float64 {
	took := 0.0
	v.wet, v.full = 0, 0
	for y := 0; y < Rows; y++ {
		for x := 0; x < Cols; x++ {
			i := y*Cols + x
			v.film[i] = math.Min(Sun, v.surf.At(x, y))
			dry := v.bed.Draw(sim.Coord{X: x, Y: y}, 0, terra.Cell{Moisture: Sun - v.film[i]})
			took += v.film[i]
			took += dry.Moisture
			if lift := v.film[i] + dry.Moisture; lift > 0 {
				v.wet++
				if lift >= Sun {
					v.full++
				}
			}
		}
	}
	v.surf.Apply(func(x, y int, w float64) float64 { return w - v.film[y*Cols+x] })
	v.gone += took
	return took
}

// step is one tick of the water cycle, in the order it happens: the
// spring puts water in, the ground under it drinks what it can hold,
// what is left runs downhill, and the sun lifts its allowance back
// out.
func (v *valley) step() float64 {
	v.tick++
	v.surf.Add(v.spring.X, v.spring.Y, v.rate)
	v.added += v.rate
	v.soak()
	v.surf.Sweep(v.flow)
	lifted := v.sun()
	v.chain.AddStore(v.surf)
	v.chain.AddBits(v.bed.Bits())
	return lifted
}

// held is the water still in the valley: the film on top of the
// ground plus the moisture inside it.
func (v *valley) held() float64 { return v.surf.Total() + v.bed.Held().Moisture }

// check is the books: everything put in, less everything still here,
// less everything taken out. Nothing else may move water, so this is
// zero or there is a bug.
func (v *valley) check() float64 { return v.added - v.held() - v.gone }

Both transfers in that tick are written the same way and it is not a coincidence. Ask one owner for a number, take the number it gives back, apply it to the other side. soak asks the bed and the film pays what the bed accepted; sun asks the bed and the sky is credited with what the bed gave up. The second is Draw with a radius of zero, which is worth a moment: a root and an afternoon of sunshine take water out of the ground by exactly the same operation, and the difference between them is only where the water goes next.

check costs three reads and two subtractions, and it is the most valuable line in the program.

▣ Build · stage 6 — sixty ticks, and the water gets to the pond
$ go run ./cmd/soak -ticks 60 -every 10 -from 60 -to 60
soak: water as a quantity on the seed 5 valley, counted in and counted out
      the spring at (10,1) adds 0.80 a tick, the sun lifts 0.02 from every wet cell
      soil takes 0.05 a tick and holds 1.00; a cell hands its film to the ground below it
      # rock   . dry   , damp   : wet ground   o standing water   O deeper than the soil holds
  tick     added      film      soil      gone        check   cells         water
     0      0.00      0.00      0.00      0.00    0.000e+00       0  ef115a0e0c15
    10      8.00      1.69      4.02      2.29   -4.441e-16      15  bff25d98e2e8
    20     16.00      1.69      9.26      5.04    1.776e-15      15  9669d132e4af
    30     24.00      2.79     12.62      8.58    5.329e-15      27  106569263717
    40     32.00      3.75     14.82     13.43    1.066e-14      30  b97806907801
    50     40.00      4.87     15.99     19.14   -2.487e-14      34  84ba398687e5
    60     48.00      6.06     16.52     25.42   -5.684e-14      36  2ea804af362e
        ############
        #.....,:oo:#
        #.....:oooo#
        #...,oooooo#
        #..,,ooooo:#
        #...,ooo,..#
        #..........#
        ############
      48.00 in, 22.58 still here, 25.42 gone up; the sun took 0.651 on the last tick
      the sun took from 36 cells on that tick, 30 of them holding enough to pay in full
      the books, to every digit they have: -5.684341886080802e-14
      every tick hashed and folded together: d83216798a5602ce

Sixty ticks, forty-eight units delivered into one cell on the rim, and the water has taken the route chapter 34's fan took, because it is the same table deciding. Then it went further: the standing water reaching into rows 3 and 4 is the basin taking its first delivery. The count the count to inspect is the third column against the fifth. The valley is holding 22.58 units and has already lost 25.42, so more than half of everything the spring ever produced has left the world entirely, and no earlier version of this world could have told you that. Water leaves.

The check column is doing its job in the least dramatic way available. The last of those residuals is −5.684 × 10−14, and it is useful to recognize that number by sight. Adjacent float64 values near 48 are 7.105 × 10−15 apart, so the books are out by exactly eight of the smallest steps the arithmetic can take at that size: what you get for adding a hundred numbers in one order and a hundred in another. A residual the size of rounding is rounding. A residual the size of water is a leak.

▣ Build · stage 7 — twelve hundred ticks, and the valley stops changing
$ go run ./cmd/soak -ticks 1200 -every 200 -from 1200 -to 1200
soak: water as a quantity on the seed 5 valley, counted in and counted out
      the spring at (10,1) adds 0.80 a tick, the sun lifts 0.02 from every wet cell
      soil takes 0.05 a tick and holds 1.00; a cell hands its film to the ground below it
      # rock   . dry   , damp   : wet ground   o standing water   O deeper than the soil holds
  tick     added      film      soil      gone        check   cells         water
     0      0.00      0.00      0.00      0.00    0.000e+00       0  ef115a0e0c15
   200    160.00     19.24     17.98    122.78    4.263e-14      38  c8e50a188d69
   400    320.00     32.95     19.98    267.07    2.103e-12      41  44c2f1b3eda4
   600    480.00     35.66     21.02    423.32    4.604e-12      42  e9c11423dbf6
   800    640.00     35.74     21.41    582.85   -2.728e-12      42  4ceea24d3dd5
  1000    800.00     35.76     21.82    742.41   -1.137e-11      42  766c85ab7b7a
  1200    960.00     35.93     21.98    902.09   -2.046e-11      43  46e1b3b2bffa
        ############
        #.....::oo:#
        #...::ooooo#
        #.OOOOOoooo#
        #.OOOOOOoo:#
        #...OOOOo..#
        #...O,:o...#
        ############
      960.00 in, 57.91 still here, 902.09 gone up; the sun took 0.800 on the last tick
      the sun took from 43 cells on that tick, 39 of them holding enough to pay in full
      the books, to every digit they have: -2.0463630789890885e-11
      every tick hashed and folded together: 729363b91fb2c453

Lay the deep cells of that map over the terrain the seed drew back in chapter 3 and they are the same cells, row for row: five across on row 3, six on row 4, four on row 5, one on row 6. Nobody told the water where the pond was. The pond is the set of cells at height 0, and the arithmetic put the water at the bottom because the bottom is where two clauses of one function send it. Those cells hold no soil moisture at all, and the soil column is lower than it looks for that reason: the bed refuses open water, so a pond in this world is standing water and never damp ground.

The rest of the map is what chapter 34 could not have drawn. A wet fan runs from the spring down to the water, a damp fringe reaches a cell or two beyond it, and the whole column against the west wall never takes a drop, though the ground there is no higher than the ground the fan crosses. From tick 600 on, the film, the soil and the count of paying cells barely move: 960 units have gone in, the valley holds 58 of them, and the other 902 left through the sun. Eight tenths of a unit in, and on the last tick, exactly eight tenths back out.

⚠ Worked failure — the soak that ran twice, out of two different worlds

soak was not written that way first. There is a version any reader of chapter 34 would reach for, and it is prettier: work out what the ground can take, sweep the film down by it, then walk the bed and put the same amount in. It even reads as one idea instead of two, with a single function saying how much moves.

// cmd/soak/main.go — the tidy version, and the wrong one

// wants is how much the film on one cell would hand to the ground
// beneath it: what the ground can take in a tick, what the film has,
// and what is left of the cell's capacity, whichever of the three is
// least. Only the failure below calls it, because the shipped soak
// never needs the number twice.
func (v *valley) wants(x, y int) float64 {
	c := sim.Coord{X: x, Y: y}
	if !v.bed.Rootable(c) {
		return 0
	}
	room := math.Max(Cap-v.bed.Moisture(c), 0)
	return math.Min(math.Min(Soak, v.surf.At(x, y)), room)
}

func (v *valley) soakStale() {
	v.surf.Sweep(func(s *cells.Store, x, y int) float64 {
		return s.At(x, y) - v.wants(x, y)
	})
	for y := 0; y < Rows; y++ {
		for x := 0; x < Cols; x++ {
			v.bed.Soak(sim.Coord{X: x, Y: y}, v.wants(x, y), Cap)
		}
	}
}
$ go run ./cmd/soak -stale -ticks 400 -every 80
soak: water as a quantity on the seed 5 valley, the soak written as two sweeps
      the spring at (10,1) adds 0.80 a tick, the sun lifts 0.02 from every wet cell
      soil takes 0.05 a tick and holds 1.00; a cell hands its film to the ground below it
      # rock   . dry   , damp   : wet ground   o standing water   O deeper than the soil holds
  tick     added      film      soil      gone        check   cells         water
     0      0.00      0.00      0.00      0.00    0.000e+00       0  ef115a0e0c15
    80     64.00      5.94     14.98     35.56    7.521e+00      35  9e5e1e29effb
   160    128.00      8.73     14.98     90.44    1.385e+01      36  5720fbfa34be
   240    192.00     10.51     14.98    146.06    2.046e+01      36  62715c9ef260
   320    256.00     12.06     14.98    201.67    2.729e+01      36  8b6fa53aab12
   400    320.00     13.39     14.98    257.29    3.434e+01      36  b2dff0bfbe0a
      320.00 in, 28.37 still here, 257.29 gone up; the sun took 0.695 on the last tick
      the sun took from 36 cells on that tick, 34 of them holding enough to pay in full
      the books, to every digit they have: 34.34372123926016
      every tick hashed and folded together: cef81f9976dd2a75

Everything in that run looks like a valley. Water arrives, the ground wets, the map fills, thirty-six cells are paying the sun, the numbers settle down. One column disagrees. The check has climbed to 34.34, and 34.34 units against 320 delivered is better than a tenth of every drop the spring ever produced, gone somewhere the ledger cannot name.

Work backwards from the sign. The check is positive, so added exceeds what the valley holds plus what the sun took: the water was not double-counted, it was destroyed. Something is taking water off the film and putting it nowhere. Exactly one line takes water off the film without the sun's name on it, and it is that sweep.

Sweep swaps its two grids when the last cell is written. That is the point of it, and it means that the instant the sweep finishes, v.surf.At answers out of the new film and not the film the tick began with. The loop underneath then calls wants a second time, and wants asks what the film has. It gets an answer from after the water was already taken. Wherever a cell's film was thinner than the twentieth of a unit the ground could have accepted, the sweep took all of it and left nothing, the loop read nothing, and the bed gained nothing. The fringe of a wet area is made of exactly those cells, and there is a fringe every tick.

The mend is not a better ordering but a refusal to compute the number twice at all, which is what Soak returning its own answer buys: ask once, and let the reply be what both sides use. Put in the general form the hazard is older than this chapter. Chapter 7 flooded the valley in one tick because a rule read a value its own scan had already changed, and chapter 34 answered that with a sweep in which no rule may write. Two owners put it back one level up. A double buffer makes a tick an instant within one grid; across a grid and a bed, nothing protects you, and the second half of a transfer reads a world the first half has already moved on from.

▤ Note — the order the ledger cannot see

The tick soaks before it flows. Swapping those two lines is defensible: water that arrives and leaves in the same tick never stayed long enough to sink in. Run it that way to tick 1200 and the film holds 39.59 against 35.93 and the bed 22.23 against 21.98, while the spring's own cell dries out altogether — second row of the map, one in from the east rim, a dot here where the shipped run has a colon — since every unit it receives is gone downhill before the ground under it is asked:

$ go run ./cmd/soak -runfirst -ticks 1200 -every 1200 -from 1200 -to 1200
  tick     added      film      soil      gone        check   cells         water
     0      0.00      0.00      0.00      0.00    0.000e+00       0  ef115a0e0c15
  1200    960.00     39.59     22.23    898.19   -1.978e-11      43  314fd90b0d09
        ############
        #.....::oo.#
        #...:oooooo#
        #.OOOOOoooo#
        #.OOOOOOoo:#
        #...OOOOo,.#
        #...O:oo...#
        ############
      960.00 in, 61.81 still here, 898.19 gone up; the sun took 0.797 on the last tick
      the sun took from 43 cells on that tick, 39 of them holding enough to pay in full

Both orders close their books: −1.978 × 10−11 here against −2.046 × 10−11 for the shipped order, which is rounding in both. The ledger checks that no water was invented or lost; it has nothing to say about which of two boxes the water is sitting in, and choosing between them is modelling, not bookkeeping.

The valley's water budget: one way in, one way out, transfers in between A panel labelled the valley holds two stacked boxes. The upper box is the film, the water standing on the ground, with an arrow curving over it labelled runs downhill, cell to cell. An arrow leads down from the film to the lower box, the bed, labelled soaks in at five hundredths of a unit a cell each tick. To the left, outside the panel, a box labelled the spring feeds eight tenths of a unit a tick into the film. To the right, outside the panel, a box labelled the sky takes two hundredths of a unit from every wet cell, drawn from both the film and the bed. A caption along the bottom reads: added equals held plus gone, checked on every tick of every run. WHERE EVERY UNIT OF WATER IS THE VALLEY runs downhill, cell to cell FILM the water standing on the ground soaks in, 0.05 a cell a tick BED the moisture a root draws on THE SPRING 0.80 a tick THE SKY 0.02 a wet cell added = held + gone, checked on every tick of every run

Figure 37.1 — two boxes inside the valley and one door at each end. The arrow across the top moves water between cells of the film and never changes the total; only the two arrows crossing the panel wall can.

The spring's wet reach

∑ Interlude — inflow equals outflow, and what that fixes

Start with numbers. Eight tenths of a unit arrive every tick. The sun takes 0.02 from every cell with water in it, so if 30 cells are wet the valley loses 0.60 a tick, which is less than it gains; the surplus has to go somewhere, it wets more ground, and the wet area grows. If 50 cells are wet the valley loses 1.00 a tick, more than it gains, the edges dry and the wet area shrinks. Between the two is the count where the losses match the gains exactly and nothing pushes the edge either way: 0.80 divided by 0.02, which is 40 cells.

n = S / E

That is the whole of it, and it is the arithmetic behind every steady state in the rest of this volume. What a world holds stops changing when what enters in a tick equals what leaves in a tick, and if the leaving is charged per cell, the balance is a count of cells. Run the same valley at seven spring rates and put the prediction next to what happens.

$ go run ./cmd/soak -mode steady -ticks 2400
soak: the sun's allowance is 0.02 a cell a tick, and the valley has 60 cells that are not rock
  spring   predicted   cells   paying in full   the sun took   held at 1200   held at 2400
    0.20        10.0      13                8          0.200          8.22          8.22
    0.40        20.0      26               18          0.400         16.08         16.08
    0.60        30.0      36               28          0.600         20.72         20.72
    0.80        40.0      43               39          0.800         57.91         58.00
    1.00        50.0      51               49          0.997        116.90        129.09
    1.20        60.0      59               58          1.180        203.74        268.67
    1.40        70.0      60               60          1.200        363.82        603.82

On the first four rows the sun's take is the spring rate to three decimals, and the prediction lands between the two counted columns every time. At a spring of 0.20 the arithmetic says ten cells and the valley settles with thirteen paying something and eight of them paying the full allowance: eight times 0.02 is 0.16, the other five hand over 0.04 between them, and the total is 0.200. The prediction is a count of full-price cells, and the valley pays it partly in whole shares and partly in the trickle at the wet edge, where everything that arrives evaporates the same tick.

The bottom rows are where the formula runs out of valley. Only 60 cells are not rock, so the most the sun can lift is 60 times 0.02, which is 1.20 a tick, and a spring stronger than that is asking for more wet ground than exists. Read the last row slowly. The sun takes exactly 1.200, and the valley's holdings go from 363.82 at tick 1,200 to 603.82 at tick 2,400: 240 units in 1,200 ticks, which is 0.20 a tick, which is precisely the difference between the spring's 1.40 and the valley's ceiling of 1.20. It never reaches a steady state. It gets deeper, at a rate you can work out before running anything.

Swhat the spring adds to the valley in one tick, 0.80 in the runs above
Ethe sun's allowance: what it lifts off one wet cell in one tick, 0.02 here
nthe count of full-price wet cells the inflow can support at steady state
n × Ethe whole valley's outflow in one tick, which is the number S has to match

The water top

Three chapters of this book have now solved the same problem three ways, and the third is what this one adds. An accumulator lets any force push on a body without knowing what else is pushing. A sweep lets any rule decide a cell without knowing which cells have already been decided. A transfer lets two owners agree about a quantity moving between them, and its mechanism is smaller than either of the others: one of the two owners works the amount out and reports it, and the other applies what it was told. Two functions that should return the same number will one day not.

The ledger is the other half, and it is cheap in a way that is easy to underrate. Three reads, two subtractions, one printed column. It does not know what water is and it cannot tell you which line is wrong, and it caught a bug that had been quietly eating a tenth of the valley behind a picture that looked correct. Any quantity in this world that is neither created nor destroyed is owed the same column: nutrients in the soil, energy in a creature, coins in somebody's purse. Once a program can state where all of something is, the bugs that were always there become findable in a glance instead of an afternoon.

Three of this tick's four laws touch only the cell they are writing, and one reads its neighbours. That single difference decides which needs the double buffer, which can run in place, and where a hazard can hide, so it is the first question to ask about any new law. The film's flow rule needed a sweep. The spring, the soak and the sun did not, and giving them one would have cost a copy of the grid to guard against an ordering no cell can see.

One last property of these runs, easy to miss because nothing announces it: there is no seed on this page. Fire drew from a stream and the sickness drew from another one, but water is arithmetic all the way down, so the chain digest 729363b91fb2c453 is a fact about the rates rather than about a generator. It folds the film and the bed together at every tick, so it covers everything a law here can touch. Change the sun's allowance in its fourth decimal place and the digest changes. That makes the chain the cheapest regression test available for a physical law: run it, keep the digest, and any edit that alters the world by one bit anywhere in twelve hundred ticks says so immediately.

The seedling's cell

The bed holds a number a root can spend now, and it is not the same number everywhere. Take three cells a seed might blow onto, run the valley to its steady state, and ask each of them the previous chapter's question: Offer at a radius of 1, which is the cell itself and the four beside it, and which reports only the cells a root could actually live in. Then shut the spring off, leave the sun running, and watch what each site has left.

▣ Build · stage 8 — three addresses, and a valley with the tap turned off
$ go run ./cmd/soak -mode sites -ticks 1200
soak: 1200 ticks of spring, then none, seed 5 valley
      a root reaches its own cell and the four around it, so five cells of soil,
      and a cell of soil holds 1.00 when it is full

  site               ground   soil in reach   first wet   the film on its own cell
  the shoulder (2,1)        2           0.000       never                      0.000
  the run (8,2)             3           5.000      tick 3                      0.230
  the shore (8,5)           1           2.000     tick 26                      0.510

      the spring shuts off at tick 1200 and the sun keeps its allowance
  tick   the shoulder      the run    the shore     valley held
  1200          0.000        5.000        2.000           57.91
  1220          0.000        3.200        1.767           42.43
  1240          0.000        1.200        0.967           27.23
  1260          0.000        0.000        0.167           14.46
  1280          0.000        0.000        0.000            7.74
  1300          0.000        0.000        0.000            1.34
  1320          0.000        0.000        0.000            0.00
      the run's five cells empty at tick 1253, the shore's at 1265, the valley at 1305

The shoulder is the result to sit with. It stands at height 2 and the run stands at height 3, so the cell that never sees a drop in twelve hundred ticks is the lower of the two. Height was never the question. The question is whether water passes through, and this water comes out of one cell on the north-east rim and takes the ways down the heights offer it, which do not go west. A seed landing on the shoulder has ground that is perfectly good in every respect except the one that decides.

The shore is the other kind of disappointment, and it is the previous chapter's rule doing the damage. It sits one step above the water with a full film on it, and three of the five cells in its reach still give it nothing: one is open water, which no root can live in, and two are ground the fan never got to. A seedling there has a pond in sight and 2.000 of soil to drink, against the run's 5.000 on ground that never floods.

Now the drying. The run's five cells are full at tick 1,200 and empty at 1,253. Predicting that is arithmetic anyone can do: five cells at 0.02 each per tick is 0.10 a tick, and 5.00 divided by 0.10 is 50 ticks. The measured answer is 53, and the extra three ticks are the film. The sun takes its allowance off standing water first and only reaches the ground when the film is gone, so the 0.230 sitting on that cell buys the bed beneath it a few ticks of grace, and so does every film in reach of it. The shore starts with less than half as much and outlasts it anyway, to tick 1,265, because the pond spends those ticks draining through it. Same rule, same rates, different address.

Then the last column empties the place: 57.91 units at tick 1,200, nothing at all by 1,305, and every one of them left through the allowance that had been quietly taking 0.02 a cell all along. The sun has been a colour in the sky for thirty-six chapters. It has a job now, it is the only door out of the valley, and its allowance is one constant that nothing in this world yet turns up or down.

✓ Checkpoint — water you can count
  • Given a cell's ground height and the film on it, you can work out the top of its water, say which of its four neighbours it hands anything to, and say why the two uphill ones get nothing.
  • say why a transfer should be worked out by one of its two owners and reported to the other, and what a ledger prints when both ends work it out separately.
  • Given an inflow rate and a per-cell evaporation allowance, you can predict the wet area at steady state, and explain why the count of cells paying in full comes out under the prediction.
  • Shown a non-zero check column, you can tell a rounding residual from a leak by its size, and read its sign to say whether water was created or destroyed.
  • look at a new law and say whether it needs a double-buffered sweep or can run in place, from whether it reads any cell but its own.
  • Shown two sites on this map, you can say which one holds moisture without measuring anything but the heights, the route the water takes, and which cells a root can live in.
⚡ Exercises — try first, then reveal
Exercise 1 — how long does a valley take to dry? From the tick-1200 ledger alone (57.91 units held, the sun taking 0.800 that tick), predict how many ticks the valley needs to empty once the spring stops. Then read the last column of the sites run.

Dividing 57.91 by 0.800 gives 72.4 ticks, and the measured answer is 105. The estimate assumes the outflow holds at eight tenths of a unit a tick, and it cannot: the sun's take is 0.02 per wet cell, so the moment cells start emptying, the valley loses its ability to lose water. The held column shows it plainly, dropping about 15 units per twenty ticks at first and under 7 per twenty ticks by tick 1,280. A quantity whose outflow falls as it drains approaches empty slowly, which is the same arithmetic as a cooling cup of tea, and the reason a drought punishes the last wet cell far longer than the missing rainfall suggests.

Exercise 2 — take the levelling clause off the receiving end. Suppose the receiving side of the exchange were written out by hand instead of asked for: the same proportional share, without the clause that stops a giver overfilling its neighbour. Predict what the ledger does, then run it.

Every cell where the levelling clause binds now hands over less than its neighbours believe they are receiving, and the difference is water nobody had. The check goes negative, which is the sign that says created rather than destroyed, and since the invented water is handed on next tick and invented again, the error compounds:

$ go run ./cmd/soak -takeall -ticks 120 -every 20
  tick     added      film      soil      gone        check   cells         water
     0      0.00      0.00      0.00      0.00    0.000e+00       0  ef115a0e0c15
    20     16.00      1.69      9.26      5.04    1.776e-15      15  9669d132e4af
    40     32.00     49.52     14.94     15.31   -4.777e+01      42  3dc86f22e680
    60     48.00  93847.40     39.60     38.19   -9.388e+04      60  fdb6020b1a41
    80     64.00 216275752.20     44.00     62.19   -2.163e+08      60  2dea877ed5c7
   100     80.00 513221372699.32     44.00     86.19   -5.132e+11      60  795dc3972f7d
   120     96.00 1228741233714069.50     44.00    110.19   -1.229e+15      60  5e8dae2a2efc

Tick 20 is byte-identical to the working run: the digest 9669d132e4af matches, because until some cell's film is thick enough for the levelling clause to bind, the two versions compute the same numbers. Print every fourth tick instead and you can watch the first non-zero check appear on its own. By tick 40 the books are out by 47.77 against 32 delivered, by tick 120 the film holds 1.2 × 1015 units against 96, all sixty cells are under water, and the sun is taking its ceiling of 1.20 a tick against a flood multiplying by more than two thousand in each twenty-tick step. The lesson is in the middle rows, not the last: a conservation bug that has not exploded yet is still a conservation bug, and catching it while the number is small takes a column that was printed all along.

Exercise 3 — move the spring to the west side. Run the spring from (3, 1) instead of (10, 1), the same eight tenths of a unit a tick. It stands two steps above the pond instead of six. Predict whether the valley ends up wetter, drier or the same, then check.

It holds twice as much water and wets less ground. Twelve hundred ticks from (3, 1) leave 115.00 units in the valley against 57.91 from (10, 1), across 39 paying cells instead of 43, and the sun is taking 0.778 a tick against 0.800, so this run has not even reached its steady state yet:

$ go run ./cmd/soak -src 3,1 -ticks 1200 -every 400 -from 1200 -to 1200
  tick     added      film      soil      gone        check   cells         water
     0      0.00      0.00      0.00      0.00    0.000e+00       0  ef115a0e0c15
   400    320.00     56.22     20.00    243.78    2.188e-12      37  c9c7d7777ef2
   800    640.00     78.94     21.00    540.06   -1.705e-12      38  479c614df9dc
  1200    960.00     93.00     22.00    845.00   -1.739e-11      39  fabcea54c932
        ############
        #.oOoo:....#
        #oOOOOO....#
        #OOOOOOO...#
        #OOOOOOOO..#
        #.OOOOOOO..#
        #..OOOOO...#
        ############
      960.00 in, 115.00 still here, 845.00 gone up; the sun took 0.778 on the last tick

The difference is the trip. Water from (10, 1) has six steps of ground to descend and every cell it crosses takes its twentieth of a unit and keeps it; water from (3, 1) is in the basin after two, and goes deep instead of wide. Deep water is expensive: it pays the same 0.02 a cell as damp ground, so a valley stacking its water in one corner evaporates more slowly than it could and takes longer to find a balance. The entire east half of that map is dry, and it is dry with more water in the valley, not less.

The ground under The Hollow now holds an amount that arrives somewhere in particular, moves, and runs out, which settles one of the two things a plant spends its day collecting. The other arrives from straight overhead, costs nothing, falls in equal measure on every cell of the grid, and belongs entirely to whichever plant grows tall enough to stand in the way of it.