using Godot; /// /// One preset's input knots for the v5 curve. Two presets exist for the task-09 /// taste batch — COMPACT (maximum lowland, cordillera-from-plains) and BALANCED /// (the task-07/08 lineage, gradual highland approach). The loser retires after /// the developer's gate; the winner becomes plain "v5". /// public sealed class CurveKnots { public readonly byte PresetId; public readonly string Name; public readonly float K1, K2, K3, K4, K5, K6; public CurveKnots(byte id, string name, float k1, float k2, float k3, float k4, float k5, float k6) { PresetId = id; Name = name; K1 = k1; K2 = k2; K3 = k3; K4 = k4; K5 = k5; K6 = k6; } } /// /// The height-redistribution curve, v5 (terrain-water task 09) — v4's spatially /// modulated shelves plus the THREE CORNER FIXES, with PRESET-PARAMETERIZED knots. /// Pure, static, monotonic; every per-column input and the knot set are explicit /// PARAMETERS (D-035). /// /// The corner fixes (both presets — the naturalness work): /// 1. Riser endpoint slope floor 0.2 → 0.1 (blend 0.1u + 0.9·smoothstep): climbs /// decelerate into shelves and accelerate out of them — no machined edges. /// 2. Summit-spike base floor 0.1 → 0.05 (blend 0.05u + 0.95·u⁴): the spike /// leaves the plateau gently — no hard skirt under the peaks. /// 3. SHELF_SPAN_MIN 2 m → 6 m: pronounced shelves keep a gentle tilt — flat to /// build on, never snooker-table flat. /// /// Presets (input land-fraction targets; knots calibrated 2026-08-08 from the same /// pooled batch-04 flat-sea land CDF as tasks 05/07, 340,618,126 samples; achieved /// fractions exact by construction): /// COMPACT — 65/12/7/4/6/3/3 (orange/red/foothill-riser/bench/mid-riser/plateau/spike) /// BALANCED — 60/13/10/5/8/3/1 /// /// Unchanged from v4: storm-ladder anchors, bench 100±12 m, plateau 220±20 m, /// strength modulation (span max 25 m), 420 m cap, per-seed spike normalization, /// modulation fields/seed offsets, the classify-map invariant. /// public static class HeightCurve { public const ushort VERSION = 5; // The task-09 taste gate's WINNER: BALANCED (id 2). COMPACT retired with the // verdict; its knots survive only in the task-09 report/batch for the record. public static readonly CurveKnots V5 = new CurveKnots(2, "balanced", 0.515899f, 0.612157f, 0.710472f, 0.784045f, 0.962922f, 1.119118f); // P60/73/83/88/96/99 // Fixed output anchors — storm ladder + ceiling (frozen). public const float SEA = 0.15f; public const float ORANGE_CEIL = 0.206f; public const float RED_CEIL = 0.27f; public const float PEAK_CAP = SEA + 420f / 251f; public const float TAIL_SLOPE = 0.25f; public const float SPIKE_MIN_SPAN = 0.01f; // Modulated shelf anchors (unchanged from v4). public const float BENCH_BASE = SEA + 100f / 251f; public const float BENCH_AMP = 12f / 251f; public const float PLATEAU_BASE = SEA + 220f / 251f; public const float PLATEAU_AMP = 20f / 251f; // Corner fix 3: pronounced shelves keep ~6 m of tilt across the shelf band. public const float SHELF_SPAN_MIN = 6f / 251f; // ≈ 0.0239 (v4: 0.008 ≈ 2 m) public const float SHELF_SPAN_MAX = 0.10f; // Modulation-field derivation (unchanged from v4). public const int BENCH_SEED_OFFSET = 7101; public const int PLATEAU_SEED_OFFSET = 7207; public const int STRENGTH_SEED_OFFSET = 7303; public const float ELEV_FREQ_ISLANDS = 3.0f; public const float STRENGTH_FREQ_ISLANDS = 5.0f; public static float EffectiveSpikeMax(float hMaxSeed, CurveKnots k) { return Mathf.Max(hMaxSeed, k.K6 + SPIKE_MIN_SPAN); } public static float ShelfSpan(float strength01) { return Mathf.Lerp(SHELF_SPAN_MAX, SHELF_SPAN_MIN, Mathf.Clamp(strength01, 0f, 1f)); } public static float Apply(float h, float hMaxSeed, float benchLo, float benchSpan, float plateauLo, float plateauSpan, CurveKnots k) { if (h <= SEA) return h; float u, s; if (h < k.K1) { u = (h - SEA) / (k.K1 - SEA); s = 0.3f * u + 0.7f * (u * (2f - u)); // frozen ease-out toe return SEA + s * (ORANGE_CEIL - SEA); } if (h < k.K2) { u = (h - k.K1) / (k.K2 - k.K1); return ORANGE_CEIL + u * (RED_CEIL - ORANGE_CEIL); // frozen linear rise } if (h < k.K3) { u = (h - k.K2) / (k.K3 - k.K2); s = 0.1f * u + 0.9f * (u * u * (3f - 2f * u)); // foothill riser — corner fix 1 return RED_CEIL + s * (benchLo - RED_CEIL); } if (h < k.K4) { u = (h - k.K3) / (k.K4 - k.K3); return benchLo + u * benchSpan; // bench (min span 6 m — fix 3) } float benchTop = benchLo + benchSpan; if (h < k.K5) { u = (h - k.K4) / (k.K5 - k.K4); s = 0.1f * u + 0.9f * (u * u * (3f - 2f * u)); // mid riser — corner fix 1 return benchTop + s * (plateauLo - benchTop); } if (h < k.K6) { u = (h - k.K5) / (k.K6 - k.K5); return plateauLo + u * plateauSpan; // plateau } float plateauTop = plateauLo + plateauSpan; float spikeMax = EffectiveSpikeMax(hMaxSeed, k); if (h < spikeMax) { u = (h - k.K6) / (spikeMax - k.K6); s = 0.05f * u + 0.95f * (u * u * u * u); // summit spike — corner fix 2 return plateauTop + s * (PEAK_CAP - plateauTop); } return PEAK_CAP + (h - spikeMax) * TAIL_SLOPE; } /// /// Per-generation numeric strict-monotonicity check of the EFFECTIVE curve for /// the selected preset: all 8 modulation-extreme corners × per-seed spikeMax. /// The corner fixes lower the slope floors (risers 0.1, spike base 0.05) — the /// sweep proves they stay strictly positive everywhere. Loud throw on failure. /// public static void AssertMonotonic(float hMaxSeed, CurveKnots k) { float[] benchLos = { BENCH_BASE - BENCH_AMP, BENCH_BASE + BENCH_AMP }; float[] plateauLos = { PLATEAU_BASE - PLATEAU_AMP, PLATEAU_BASE + PLATEAU_AMP }; float[] spans = { SHELF_SPAN_MIN, SHELF_SPAN_MAX }; foreach (float bl in benchLos) { foreach (float pl in plateauLos) { foreach (float sp in spans) { float prevH = -7f; float prev = Apply(prevH, hMaxSeed, bl, sp, pl, sp, k); void Check(double hd) { float h = (float)hd; if (h <= prevH) return; // dedupe float32 samples (task-05 fix) float v = Apply(h, hMaxSeed, bl, sp, pl, sp, k); if (v <= prev) throw new System.InvalidOperationException( $"[HeightCurve] MONOTONICITY VIOLATION at h={h} (preset {k.Name}, hMaxSeed={hMaxSeed}, benchLo={bl}, plateauLo={pl}, span={sp}): {v} <= {prev}. Refusing to generate."); prev = v; prevH = h; } double top = System.Math.Max(2.0, EffectiveSpikeMax(hMaxSeed, k) + 0.5); for (double hh = -7.0 + 0.01; hh < 0.10; hh += 0.01) Check(hh); for (double hh = 0.10; hh <= top; hh += 0.0001) Check(hh); for (double hh = top + 0.05; hh <= top + 6.0; hh += 0.05) Check(hh); } } } GD.Print($"[HeightCurve] Monotonicity assertion passed (v{VERSION} preset '{k.Name}', 8 modulation corners, effective spikeMax {EffectiveSpikeMax(hMaxSeed, k):F6})."); } }