105 lines
4.5 KiB
Go
105 lines
4.5 KiB
Go
package coast
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"salty/terrain/internal/field"
|
|
)
|
|
|
|
// paintedSeaFixture is a straight coast whose whole sea is painted at one depth, the way a planet's ocean
|
|
// class is. The sea is made wide enough to hold the derived margin and a stretch of open water past it, so
|
|
// the test can ask the question that matters: how much of what the author painted survives.
|
|
func paintedSeaFixture(w, h, split int, cellM, backshoreM, paintedM float64) (*field.Field, []bool, []float32) {
|
|
f, sea := coastFixture(w, h, split, cellM, backshoreM)
|
|
abyss := make([]float32, w*h)
|
|
for i := range abyss {
|
|
if sea[i] {
|
|
abyss[i] = float32(paintedM)
|
|
}
|
|
}
|
|
return f, sea, abyss
|
|
}
|
|
|
|
// TestTheDerivedMarginDoesNotSwallowThePaintedOcean is the D-64 regression, stated as the property rather
|
|
// than as the number that was wrong.
|
|
//
|
|
// The sea floor near a shore is derived and the sea floor away from it is the painting; the break depth is
|
|
// what joins them. Set the break far shallower than the paint and the join stops being a join: the derived
|
|
// profile is then a shallow bench that runs from the waterline out to the full reach of the margin, and on a
|
|
// planet whose straits are narrower than twice that reach it *is* the ocean. That is not visible in a profile
|
|
// test - the shape is monotone and correct at any break depth - so this measures the volume instead.
|
|
//
|
|
// 512 m is the first template's `ocean` class. 30 m was the inherited square-canvas break, 130 m is the
|
|
// planet default.
|
|
func TestTheDerivedMarginDoesNotSwallowThePaintedOcean(t *testing.T) {
|
|
const w, h, split = 1400, 20, 1200
|
|
const cellM, painted = 8.0, 512.0
|
|
cfg := shelfOnlyCfg()
|
|
|
|
measure := func(breakM float64) (shallow float64, atBreak, far float64) {
|
|
f, sea, abyss := paintedSeaFixture(w, h, split, cellM, 5, painted)
|
|
Build(Input{Height: f, Sea: sea, SeaLevelM: 0, BreakM: breakM, AbyssM: painted,
|
|
Abyss: abyss, Seed: 7, Cfg: cfg})
|
|
y := h / 2
|
|
n, under50 := 0, 0
|
|
for x := 0; x < split; x++ {
|
|
n++
|
|
if -float64(f.Data[y*w+x]) < 50 {
|
|
under50++
|
|
}
|
|
}
|
|
// Just inside the widest shelf, and well past the shelf and the slope together.
|
|
shelfCells := int(cfg.ShelfKm.Hi()*1000/cellM) - 2
|
|
reachCells := int((cfg.ShelfKm.Hi() + cfg.SlopeKm) * 1000 / cellM)
|
|
return float64(under50) / float64(n),
|
|
-float64(f.Data[y*w+split-1-shelfCells]),
|
|
-float64(f.Data[y*w+split-1-reachCells-20])
|
|
}
|
|
|
|
oldShallow, oldBreak, oldFar := measure(30)
|
|
newShallow, newBreak, newFar := measure(130)
|
|
t.Logf("break 30 m: %.0f%% of this sea shallower than 50 m, %.0f m at the break, %.0f m offshore",
|
|
oldShallow*100, oldBreak, oldFar)
|
|
t.Logf("break 130 m: %.0f%% of this sea shallower than 50 m, %.0f m at the break, %.0f m offshore",
|
|
newShallow*100, newBreak, newFar)
|
|
|
|
// Both must reach the painting in open water: the margin is a join, never a replacement.
|
|
for _, c := range []struct {
|
|
name string
|
|
far float64
|
|
}{{"30 m", oldFar}, {"130 m", newFar}} {
|
|
if c.far < painted-2 {
|
|
t.Errorf("break %s: open water is %.0f m, want the painted %.0f m", c.name, c.far, painted)
|
|
}
|
|
}
|
|
// The break is where it was asked for, which is what makes it the knob worth having.
|
|
if newBreak < 110 || newBreak > 140 {
|
|
t.Errorf("the shelf break is at %.0f m, want about 130 m", newBreak)
|
|
}
|
|
// And the shallow bench shrinks. This is the whole defect: at a 30 m break every cell of the derived
|
|
// margin is shallower than 50 m by construction, so the bench is as wide as the margin reaches.
|
|
if !(newShallow < oldShallow*0.75) {
|
|
t.Errorf("shallow water is %.0f%% of the sea at a 130 m break against %.0f%% at 30 m; deepening the "+
|
|
"break has to shrink the bench or it is not doing anything", newShallow*100, oldShallow*100)
|
|
}
|
|
}
|
|
|
|
// TestAPaintedShallowStraitIsStillShallow is the other half, and it is what stops the fix above from being a
|
|
// blunt instrument: the break can never be deeper than the water it is a break in. An author who paints a
|
|
// 20 m surf class gets 20 m of water, not a 130 m trench dug through it.
|
|
func TestAPaintedShallowStraitIsStillShallow(t *testing.T) {
|
|
const w, h, split = 1400, 20, 1200
|
|
const cellM, painted = 8.0, 20.0
|
|
f, sea, abyss := paintedSeaFixture(w, h, split, cellM, 5, painted)
|
|
Build(Input{Height: f, Sea: sea, SeaLevelM: 0, BreakM: 130, AbyssM: painted,
|
|
Abyss: abyss, Seed: 7, Cfg: shelfOnlyCfg()})
|
|
|
|
y := h / 2
|
|
for x := 0; x < split; x++ {
|
|
if d := -float64(f.Data[y*w+x]); d > painted+1 {
|
|
t.Fatalf("%.0f m offshore: %.1f m of water over a sea painted at %.0f m",
|
|
float64(split-x)*cellM, d, painted)
|
|
}
|
|
}
|
|
}
|