using System.Text; namespace IslaApocalypse.Tools { /// Which offshore-islet system pass 1 runs. → . public enum OffshoreMode { /// No islets. The shelf is gated separately (TerrainGenConfig.CoastShelf). Off, /// /// ⭐ chat2/05 stage 1 — the reference's probabilistic layer, verbatim: one noise field, one /// calibrated threshold, the faithful blob, the faithful zone mask. Sparse, no corners, no /// guards. THE CONTROL. /// Faithful, /// /// ⭐ chat2/06 — THE ONE ISLAND MECHANISM: the organic noise-field layer, reshaped small / /// low / flat / crisp (chat2/05 stage 2's shape, unchanged), tuned for coverage by DENSITY /// and a SOUTH WEIGHT, with the speck / separation / blob guards. No seeded floor, no /// stamps, no count guarantee — every island is a noise outline, and the per-hemisphere /// counts are a statistical outcome of the tuning (the chat2/05 forced floor was tried and /// reverted on look; it is in git history, one checkout away). /// Organic, } /// /// ⭐ EVERY OFFSHORE-ISLET DIAL, IN ONE OBJECT — config-gated and isolated, per the developer's /// standing modularity concern. Nothing above hardcodes an island /// specific; the whole system is this object + IslandFalloff + OffshorePass. /// /// ═══ TWO PRESETS, AND WHY BOTH EXIST ═══ /// /// the reference's constants, verbatim. The control in every batch. /// the reshape + the chat2/06 coverage tuning — the deliverable. /// /// The reshape does not EDIT the faithful constants; it sets different values on the same /// dials. So Faithful() stays bit-reproducible however far the organic layer is tuned. /// /// ═══ THE TWO PROTECTIONS THAT ARE NOT DIALS ═══ /// /// (the moat) and (the "actually offshore" test) /// are exposed here because every knob is, but they are the MAIN-ISLAND AND LAKE PROTECTIONS and /// the presets do not move them. They are what makes "no island can bridge to shore" and "no /// island in a lake or the crater bay" true by construction. /// /// ═══ ⚠ WHAT IS DELIBERATELY NOT HERE (chat2/06) ═══ /// /// No floor, no stamp radius/core/jitter, no separation-of-seeded-centres, no placement RNG — /// nothing that guarantees a count or places an island from a centre. The forced mechanism /// (chat2/05 Hybrid()) was reverted out whole; if a hard count ever comes back it is a /// design decision, not a knob that was left lying around. /// public sealed class OffshoreSettings { public OffshoreMode Mode = OffshoreMode.Off; // ---- the organic (noise) layer ---------------------------------------- /// Islet noise frequency, periods per map width. Higher ⇒ SMALLER blobs. Reference 14. public float FreqPerMapWidth = IslandFalloff.OFFSHORE_FREQ_ISLANDS; /// Islet noise seed offset. Reference 7607. public int SeedOffset = IslandFalloff.OFFSHORE_SEED_OFFSET; /// /// ⭐ THE MAIN KNOB — organic density: the fraction of the noise field's ACTUAL sampled /// distribution that clears the calibrated threshold. More ⇒ more of the field becomes /// island. This is the NORTH density; the south is this × . /// Reference 0.02. /// public float Density = 0.02f; /// /// ⭐ THE SOUTH WEIGHT — a per-hemisphere density bias. South density = /// × this. 1 ⇒ one dial (the reference); > 1 ⇒ "weighted south", /// the developer's preference. It is applied to the DENSITY (i.e. the calibration quantile), /// not to the blob shape, so a south island looks exactly like a north island — there are /// simply more of them. /// public float SouthWeight = 1f; /// The effective south density. public float DensitySouth => Density * SouthWeight; /// /// Half-width of the smooth threshold blend across the hemisphere midline, as a fraction of /// the map. ⚠ Without it a north/south density difference would cut any organic island that /// straddles the midline along a dead-straight line. 0 ⇒ hard step (never wanted). /// public float HemisphereBlendHalfWidth = 0.05f; /// Islet crest, metres above sea, PRE-CURVE. Reference 34. The curve's toe squashes it lower. public float CrestM = IslandFalloff.OFFSHORE_ISLAND_H_M; /// Fraction of a blob's excess over threshold that saturates. LOWER ⇒ flatter. Reference 0.45. public float CoreFraction = IslandFalloff.OFFSHORE_CORE; /// Crest-to-sea edge sharpening exponent. 1 ⇒ the faithful smoothstep. Higher ⇒ crisper shore. public float EdgeSharpness = 1f; // ---- the slop guards (Organic only; all scale-free fractions of the map) -------------- // // More density must not buy speck-debris or merged blobs. Three rules, each a revert BY // COMPONENT MEMBERSHIP (the island goes back to seabed whole, and is untagged) — none of // them places, shapes or counts anything. 0 ⇒ that rule is off. The faithful preset has none // (the reference had no such rules). /// /// ⚠ THE SPECK GUARD. An organic island smaller than this fraction of the map's AREA is /// noise debris — a local maximum that barely cleared the threshold and surfaced a cap of a /// few cells — and is reverted to seabed and untagged. 3e-5 is ~126 cells at 2048, ~500 at /// 4096, ~2,000 cells (a ~50 m islet) at 8192. /// public float MinIslandAreaFrac = 0f; /// /// ⚠ THE SEPARATION GUARD — "enough separation that they read as distinct islands". Two /// surviving islands whose nearest shores are closer than this fraction of the map WIDTH /// read as one cluster; the SMALLER is reverted. Greedy by size, so the largest island in a /// cluster always stays. Chebyshev distance between boundary cells. /// public float MinSeparationFrac = 0f; /// /// ⚠ THE BLOB GUARD. An organic island LARGER than this fraction of the map's area is a /// superlevel region that merged several maxima into one sprawling landmass — the "blob" /// the developer does not want — and is reverted whole. Set well above the natural size at /// the preset density so it is a safety net, not a sculptor; how often it bites is reported. /// public float MaxIslandAreaFrac = 0f; // ---- the zone mask: protections + the Trench bound -------------------- /// ⭐ THE MOAT. Reference 14 m. The presets do not move it. public float MinDepthM = IslandFalloff.OFFSHORE_MIN_DEPTH_M; public float DepthFeatherM = IslandFalloff.OFFSHORE_DEPTH_FEATHER_M; /// ⭐ THE "ACTUALLY OFFSHORE" TEST on the pre-Trench falloff. Reference 0.72. The presets do not move it. public float MinFalloff = IslandFalloff.OFFSHORE_MIN_FALLOFF; public float FalloffFeather = IslandFalloff.OFFSHORE_FALLOFF_FEATHER; /// /// The outer bound, as a fraction of the half-span (map-anchored, like the Trench). Zone /// fades from INNER to zero at OUTER. Reference 0.78 / 0.86 keeps islets well off the /// Trench ramp (which starts at 0.90) and out of the corners. The reshape pushes both OUT so /// islands populate the corners and may sit over the outer edge — the developer accepts /// islands clipped by the map edge. OUTER < 1 keeps every island's centre on the playable /// map. /// public float TrenchInner = IslandFalloff.OFFSHORE_TRENCH_INNER; public float TrenchOuter = IslandFalloff.OFFSHORE_TRENCH_OUTER; // ---- presets ---------------------------------------------------------- /// The reference, verbatim. Single density, no guards, original footprint/crest/mask. public static OffshoreSettings Faithful() => new OffshoreSettings { Mode = OffshoreMode.Faithful, FreqPerMapWidth = IslandFalloff.OFFSHORE_FREQ_ISLANDS, SeedOffset = IslandFalloff.OFFSHORE_SEED_OFFSET, Density = 0.02f, SouthWeight = 1f, // ConfigManager.OffshoreIslandDensity CrestM = IslandFalloff.OFFSHORE_ISLAND_H_M, CoreFraction = IslandFalloff.OFFSHORE_CORE, EdgeSharpness = 1f, TrenchInner = IslandFalloff.OFFSHORE_TRENCH_INNER, TrenchOuter = IslandFalloff.OFFSHORE_TRENCH_OUTER, }; /// /// ⭐ THE DELIVERABLE (chat2/06) — the organic layer, reshaped (chat2/05 stage 2's shape: /// small / low / flat / crisp, corners on) and tuned for coverage: density up from 05's /// 0.007, south-weighted, guarded against specks / clusters / blobs. /// /// ⚠ TUNED BY MEASUREMENT, not by feel (chat2/06 batch — the count table over 12 seeds × /// 3 levels and the per-hemisphere diagnosis). This preset is the batch's `density_mid`. /// public static OffshoreSettings Organic() => new OffshoreSettings { Mode = OffshoreMode.Organic, FreqPerMapWidth = 16f, // a little smaller than the reference's 14 (chat2/05) Density = OrganicDensityMid, SouthWeight = OrganicSouthWeight, CrestM = 24f, // lower than the reference's 34 (pre-curve) — lands in the curve's preserved toe CoreFraction = 0.25f, // flatter than 0.45 EdgeSharpness = 2.5f, // crisper shore than the faithful smoothstep MinIslandAreaFrac = OrganicMinIslandAreaFrac, MinSeparationFrac = OrganicMinSeparationFrac, MaxIslandAreaFrac = OrganicMaxIslandAreaFrac, TrenchInner = 0.90f, TrenchOuter = 0.97f, // corners + outer edge allowed; centre stays on-map }; /// /// The three density levels of the chat2/06 batch, in one place so the tool and the preset /// cannot disagree. Mid is the preset of record. /// public const float OrganicDensityLow = 0.016f; // the edge: every seed of the 12 still clears N ≥ 2 / S ≥ 3, but N's minimum IS 2 public const float OrganicDensityMid = 0.022f; // ⭐ the preset: N min 3 / mean 6.3, S min ~15 / mean ~20 over 12 seeds at 4096 public const float OrganicDensityHigh = 0.030f; // the "how many is too many" bookend public const float OrganicSouthWeight = 1.25f; // preference, not a fix — the diagnosis found the south UN-suppressed (see the report) public const float OrganicMinIslandAreaFrac = 3e-5f; public const float OrganicMinSeparationFrac = 0.008f; public const float OrganicMaxIslandAreaFrac = 6e-4f; public OffshoreSettings Clone() => (OffshoreSettings)MemberwiseClone(); public string Describe() { if (Mode == OffshoreMode.Off) return "offshore OFF"; var sb = new StringBuilder(); sb.Append($"{Mode}: freq {FreqPerMapWidth:F0}/map density {Density:F4} × south {SouthWeight:F2} (= S {DensitySouth:F4}) · "); sb.Append($"crest {CrestM:F0} m core {CoreFraction:F2} sharp {EdgeSharpness:F1} · "); sb.Append($"guards minArea {MinIslandAreaFrac:G2} minSep {MinSeparationFrac:G2} maxArea {MaxIslandAreaFrac:G2} · "); sb.Append($"moat {MinDepthM:F0}+{DepthFeatherM:F0} m falloff {MinFalloff:F2}+{FalloffFeather:F2} trench {TrenchInner:F2}→{TrenchOuter:F2}"); return sb.ToString(); } } }