Gravity: a wider zone fingerprint, one copied-rail helper, belts kinded at generation, adjacent belts pinned
ZoneFingerprint digests rock spin and periods, orbital entities (type, position, rail), other entities by type, the pack radius, a 9x9 height grid at two times and optionally PopulatedRadius (finding fingerprint-omits). TrojanSwarmTests.BaseDigest is repinned with it on Yggdrasil at C1's generator, swarms off. BeltShapes.CopiedRail replaces StationPlacementTests.SwarmRail and the RidesRail slot filters of ChildrenWithinReachTests: a rail a belt rides that copies a body's rail is never a placed slot. The Zones fixture counts the belts the generator writes and those with no field kind, before Zone's own FieldKinds.Ensure; EveryBeltIsKindedAtGeneration pins it at zero (finding ensure-unpinned). AdjacentBeltsKeepTheirSpacing places two belts side by side and pins the second's inner edge a sibling spacing outside the first's outer edge (follow-up belt-after-belt-edge-pin). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
73d1cee9de
commit
d0fc8adb70
5 changed files with 105 additions and 37 deletions
15
tests/Aetheria.Shared.Tests/BeltShapes.cs
Normal file
15
tests/Aetheria.Shared.Tests/BeltShapes.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using System.Linq;
|
||||
using GameCult.Caching;
|
||||
|
||||
// The belt shapes a test reads back from a built zone. A swarm's rail is a copy of its host's rail (same Parent, Distance and
|
||||
// Phase), never a slot Place laid out, so the tests that walk placed orbits leave copied rails out through this one helper.
|
||||
public static class BeltShapes
|
||||
{
|
||||
public static bool CopiedRail(Zone zone, CultRecordKey orbit)
|
||||
{
|
||||
if (!zone.Planets.Values.Any(b => b is AsteroidBeltData && b.Orbit.Key.Equals(orbit))) return false;
|
||||
var rail = zone.Orbits[orbit].Data;
|
||||
return zone.Planets.Values.Where(b => !(b is AsteroidBeltData)).Select(b => zone.Orbits[b.Orbit.Key].Data)
|
||||
.Any(o => o.Parent.Key.Equals(rail.Parent.Key) && o.Distance == rail.Distance && o.Phase == rail.Phase);
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,8 @@ public sealed class ChildrenWithinReachTests : IClassFixture<ChildrenWithinReach
|
|||
private readonly ZoneWorlds _worlds = new ZoneWorlds();
|
||||
public readonly List<(uint Seed, Zone Zone)> All = new List<(uint, Zone)>();
|
||||
public ZoneGenerationSettings Settings;
|
||||
// Every belt the generator wrote, and those it wrote with no field kind, counted before Zone's own FieldKinds.Ensure runs.
|
||||
public int Belts, UnkindedAtGeneration;
|
||||
|
||||
// Seeds 7, 11 and 12 held rings inside their heavy member's Roche margin (ring-roche-final-heavy), and seeds 10, 13, 21 and 30
|
||||
// held a bodyless ring member whose own bodies sat at their node's centre (bodyless-node-places-children); seeds 1-6 show neither.
|
||||
|
|
@ -50,6 +52,11 @@ public sealed class ChildrenWithinReachTests : IClassFixture<ChildrenWithinReach
|
|||
tune?.Invoke(Settings);
|
||||
var pack = ZoneGenerator.GenerateZone(items, Settings, planetSettings, galaxy, galaxyZone,
|
||||
galaxyZone == galaxy.Entrance && galaxy.IsPrelude, false);
|
||||
foreach (var belt in pack.Planets.Select(cache.Get).OfType<AsteroidBeltData>())
|
||||
{
|
||||
Belts++;
|
||||
if (!belt.Kind.IsSet()) UnkindedAtGeneration++;
|
||||
}
|
||||
All.Add((seed, new Zone(items, planetSettings, pack, galaxyZone, galaxy)));
|
||||
}
|
||||
}
|
||||
|
|
@ -79,11 +86,12 @@ public sealed class ChildrenWithinReachTests : IClassFixture<ChildrenWithinReach
|
|||
private static Dictionary<CultRecordKey, float> BodyMass(Zone zone) =>
|
||||
zone.Planets.Values.Where(b => !(b is AsteroidBeltData)).GroupBy(b => b.Orbit.Key).ToDictionary(g => g.Key, g => g.First().Mass);
|
||||
|
||||
// The generated orbits: every body's and its ancestors', which leaves out stations, turrets, Lagrange copies and swarm rails (a copy of the host's rail, like a Lagrange copy).
|
||||
// The generated orbits: every body's and its ancestors', which leaves out stations, turrets, Lagrange copies and the copied
|
||||
// rails swarms and lanes ride (a copy of the host's rail, like a Lagrange copy; BeltShapes.CopiedRail).
|
||||
private static HashSet<CultRecordKey> Placed(Zone zone)
|
||||
{
|
||||
var placed = new HashSet<CultRecordKey>();
|
||||
foreach (var body in zone.Planets.Values.Where(b => !(b is AsteroidBeltData { RidesRail: true })))
|
||||
foreach (var body in zone.Planets.Values.Where(b => !BeltShapes.CopiedRail(zone, b.Orbit.Key)))
|
||||
for (var key = body.Orbit.Key; placed.Add(key) && zone.Orbits[key].Data.Parent.IsSet();) key = zone.Orbits[key].Data.Parent.Key;
|
||||
return placed;
|
||||
}
|
||||
|
|
@ -332,7 +340,7 @@ public sealed class ChildrenWithinReachTests : IClassFixture<ChildrenWithinReach
|
|||
foreach (var (seed, zone) in _zones.All)
|
||||
{
|
||||
var mass = BodyMass(zone);
|
||||
foreach (var (beltKey, belt) in zone.AsteroidBelts.Where(b => !b.Value.Data.RidesRail))
|
||||
foreach (var (beltKey, belt) in zone.AsteroidBelts.Where(b => !BeltShapes.CopiedRail(zone, zone.Planets[b.Key].Orbit.Key)))
|
||||
{
|
||||
belts++;
|
||||
var orbit = zone.Planets[beltKey].Orbit.Key;
|
||||
|
|
@ -363,7 +371,7 @@ public sealed class ChildrenWithinReachTests : IClassFixture<ChildrenWithinReach
|
|||
// authored width (ruling belt-fills-its-slot): it sits at the slot's midpoint where the test can spell both edges, and
|
||||
// none collapses to a line (finding belts-collapse-to-a-line). Rocks are scattered inside the belt's half width, so their
|
||||
// spread is at most the belt's width; a belt beside another belt has an edge only the generator knows, and is checked
|
||||
// against the weaker bound.
|
||||
// against the weaker bound (two adjacent belts' spacing is pinned by AdjacentBeltsKeepTheirSpacing).
|
||||
[Fact]
|
||||
public void BeltsFillTheirSlots()
|
||||
{
|
||||
|
|
@ -374,7 +382,7 @@ public sealed class ChildrenWithinReachTests : IClassFixture<ChildrenWithinReach
|
|||
{
|
||||
var mass = BodyMass(zone);
|
||||
var placed = Placed(zone);
|
||||
var beltAt = zone.AsteroidBelts.Where(b => !b.Value.Data.RidesRail).ToDictionary(b => zone.Planets[b.Key].Orbit.Key, b => b.Value.Data.Asteroids);
|
||||
var beltAt = zone.AsteroidBelts.Where(b => !BeltShapes.CopiedRail(zone, zone.Planets[b.Key].Orbit.Key)).ToDictionary(b => zone.Planets[b.Key].Orbit.Key, b => b.Value.Data.Asteroids);
|
||||
foreach (var (orbit, rocks) in beltAt)
|
||||
{
|
||||
var parent = zone.Orbits[orbit].Data.Parent.Key;
|
||||
|
|
@ -424,7 +432,7 @@ public sealed class ChildrenWithinReachTests : IClassFixture<ChildrenWithinReach
|
|||
{ Exponent = authored.Exponent, Multiplier = authored.Multiplier * .02f, Constant = authored.Constant * .02f });
|
||||
int belts = 0, filled = 0;
|
||||
foreach (var (seed, zone) in narrow.All)
|
||||
foreach (var (key, belt) in zone.AsteroidBelts.Where(b => !b.Value.Data.RidesRail))
|
||||
foreach (var (key, belt) in zone.AsteroidBelts.Where(b => !BeltShapes.CopiedRail(zone, zone.Planets[b.Key].Orbit.Key)))
|
||||
{
|
||||
var rocks = belt.Data.Asteroids;
|
||||
if (rocks.Length < 2) continue;
|
||||
|
|
@ -447,6 +455,42 @@ public sealed class ChildrenWithinReachTests : IClassFixture<ChildrenWithinReach
|
|||
|
||||
private static PlanetSettings AuthoredPlanetSettings() => AuthoredSettings.Load(RunStartTests.FindRepoRoot()).Read<PlanetSettings>("PlanetSettings");
|
||||
|
||||
// A belt's field kind is chosen at generation and saved with it (Mining Cut 3, Q16 B): every belt the generator writes,
|
||||
// annulus or swarm, carries one before any Zone reads it (finding ensure-unpinned).
|
||||
[Fact]
|
||||
public void EveryBeltIsKindedAtGeneration()
|
||||
{
|
||||
_out.WriteLine($"belts {_zones.Belts}, unkinded at generation {_zones.UnkindedAtGeneration}");
|
||||
Assert.True(_zones.All.Any(z => z.Zone.AsteroidBelts.Values.Any(b => b.Data.RidesRail)), "no swarm in the sample");
|
||||
Assert.True(_zones.Belts > 20, $"only {_zones.Belts} belts generated");
|
||||
Assert.Equal(0, _zones.UnkindedAtGeneration);
|
||||
}
|
||||
|
||||
// Two belts side by side under one parent (production never draws them; follow-up belt-after-belt-edge-pin): the second
|
||||
// belt's slot starts a sibling spacing outside the first belt's outer edge, not its rail, so their rock intervals keep a spacing.
|
||||
[Fact]
|
||||
public void AdjacentBeltsKeepTheirSpacing()
|
||||
{
|
||||
var settings = AuthoredSettings.Load(RunStartTests.FindRepoRoot()).Read<ZoneGenerationSettings>("ZoneSettings");
|
||||
// Wider than any slot, so each belt fills its slot and the second's inner edge is the slot's inner bound.
|
||||
settings.AsteroidBeltWidth = new ExponentialCurve { Exponent = 1f, Multiplier = 0f, Constant = 1e7f };
|
||||
var root = new GeneratorPlanet { Mass = 1e5f };
|
||||
Body(root, 100f);
|
||||
var first = Body(root, 0f);
|
||||
var second = Body(root, 0f);
|
||||
Body(root, 100f, .5f);
|
||||
foreach (var belt in new[] { first, second }) { belt.Belt = true; belt.Settings = settings; }
|
||||
ZoneGenerator.Place(root, AuthoredPlanetSettings(), 1e6f, float.PositiveInfinity);
|
||||
Assert.True(root.Children.Contains(first) && root.Children.Contains(second), "a belt folded");
|
||||
Assert.True(first.Distance < second.Distance, "the belts swapped order");
|
||||
Assert.True(first.BeltHalfWidth > 0 && second.BeltHalfWidth > 0, "a belt collapsed to a line");
|
||||
var q = 1 / (1 - GravityField.Reach / 2);
|
||||
var firstOuter = first.Distance + first.BeltHalfWidth;
|
||||
var secondInner = second.Distance - second.BeltHalfWidth;
|
||||
_out.WriteLine($"first {first.Distance} +- {first.BeltHalfWidth}, second {second.Distance} +- {second.BeltHalfWidth}");
|
||||
Assert.True(firstOuter * q <= secondInner * (1 + 1e-4f), $"the second belt's inner edge {secondInner} is within a spacing of the first's outer edge {firstOuter}");
|
||||
}
|
||||
|
||||
// A rosette's ring round a root with no reach limit of its own sits at half the root's 0.75 x radius window, 3/8 of the radius.
|
||||
[Fact]
|
||||
public void ASoleRootRingSitsAtThreeEighthsOfItsRadius()
|
||||
|
|
|
|||
|
|
@ -81,10 +81,6 @@ public sealed class StationPlacementTests : IDisposable
|
|||
|
||||
private static float Turns(float turns) => turns - (float) Math.Floor(turns);
|
||||
|
||||
// A Trojan swarm's rail is a copy of its host's, not a station's source.
|
||||
private static bool SwarmRail(Zone zone, CultRecordKey orbit) =>
|
||||
zone.Planets.Values.Any(b => b is AsteroidBeltData { RidesRail: true } && b.Orbit.Key.Equals(orbit));
|
||||
|
||||
[Fact]
|
||||
public void AStationSitsAtItsPlanetsL4OrL5()
|
||||
{
|
||||
|
|
@ -100,7 +96,7 @@ public sealed class StationPlacementTests : IDisposable
|
|||
foreach (var station in zone.Entities.OfType<OrbitalEntity>().Where(e => TypeOf(built.Items, e) == HullType.Station))
|
||||
{
|
||||
var orbit = zone.Orbits[station.OrbitData];
|
||||
var sources = zone.Orbits.Where(o => !ridden.Contains(o.Key) && !SwarmRail(zone, o.Key) && SameRing(o.Value, orbit)).ToList();
|
||||
var sources = zone.Orbits.Where(o => !ridden.Contains(o.Key) && !BeltShapes.CopiedRail(zone, o.Key) && SameRing(o.Value, orbit)).ToList();
|
||||
if (sources.Count == 0)
|
||||
{
|
||||
// The tutorial entrance's one exception: a station on its own orbit round a root node, beyond the node's ring
|
||||
|
|
@ -160,7 +156,7 @@ public sealed class StationPlacementTests : IDisposable
|
|||
foreach (var station in Orbitals(built).Where(e => TypeOf(built.Items, e) == HullType.Station))
|
||||
{
|
||||
var orbit = zone.Orbits[station.OrbitData];
|
||||
var sources = zone.Orbits.Where(o => !ridden.Contains(o.Key) && !SwarmRail(zone, o.Key) && SameRing(o.Value, orbit)).ToList();
|
||||
var sources = zone.Orbits.Where(o => !ridden.Contains(o.Key) && !BeltShapes.CopiedRail(zone, o.Key) && SameRing(o.Value, orbit)).ToList();
|
||||
if (sources.Count == 0) continue; // the tutorial entrance's own orbit round a root
|
||||
stations++;
|
||||
Assert.Contains(bodyOrbits, key => key.Equals(sources[0].Key));
|
||||
|
|
@ -357,7 +353,7 @@ public sealed class StationPlacementTests : IDisposable
|
|||
foreach (var station in Orbitals(built).Where(e => TypeOf(built.Items, e) == HullType.Station))
|
||||
{
|
||||
var orbit = zone.Orbits[station.OrbitData];
|
||||
var strays = zone.Orbits.Count(o => !ridden.Contains(o.Key) && !SwarmRail(zone, o.Key) && SameRing(o.Value, orbit));
|
||||
var strays = zone.Orbits.Count(o => !ridden.Contains(o.Key) && !BeltShapes.CopiedRail(zone, o.Key) && SameRing(o.Value, orbit));
|
||||
Assert.True(strays == 1, $"seed {built.Seed}: a station's ring holds {strays} orbits no entity rides, not just its source's");
|
||||
stations++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,17 +97,17 @@ public class TrojanSwarmTests
|
|||
Assert.True(onSwarmHost > 0, "no station or turret sits on a swarm host's rail");
|
||||
}
|
||||
|
||||
// The digests were computed at 67517f40, before cut trojan-swarms, over the zones of each seed with the authored settings;
|
||||
// seeds 3-6 were repinned at cut laned-belts C1, whose one belt extent rule moves stations in them. A zone with no swarm is
|
||||
// the base zone, and a zone with swarms is the base zone plus them.
|
||||
// The widened digests (ZoneFingerprint with PopulatedRadius) were computed on Yggdrasil over the generator of cut laned-belts
|
||||
// C1 (one belt extent rule) with swarms off, over the zones of each seed: a zone with no swarm is the base zone, and a zone with
|
||||
// swarms is the base zone plus them. The per-zone comparison leaves PopulatedRadius out, since a swarm's outer rock may grow it.
|
||||
private static readonly Dictionary<uint, string> BaseDigest = new Dictionary<uint, string>
|
||||
{
|
||||
[1] = "02a2bef42b5d4aeaf1e7004d0ab26620ee570ced95115abf98226ab4e0db23eb",
|
||||
[2] = "de231f392e5724ecdce7e746957633bef2724252147ecbb5830db047b635aead",
|
||||
[3] = "0377471bf1a73b4e00ee2d18a9e90960fdf7011d35e7c648f103f3538750501f",
|
||||
[4] = "195c6647ff921a12cd5ee71f94610f245e64c15fc1d38bc708846cd04e3ddeb5",
|
||||
[5] = "ae0d57e84456767d35a699941238e7574e815da663b0354e5e1676063910e8a5",
|
||||
[6] = "8ad70edd5d41e942ba9067930e0f148e3da6c054bf1d9d7cdd26be93e12017c6",
|
||||
[1] = "1873400d50d11b27913a67daca460f0829629aa21dacabac2cfc97a66537a700",
|
||||
[2] = "5872680ee91c2019b345925f54c2113303cc2817ee9ea28d26ba839e43d1944e",
|
||||
[3] = "e92cc32010f01b7962a2a58b6ea91c9c39ee3d915301164f176d0ebad218837c",
|
||||
[4] = "61db9bdae2ca8704a7946debb4fe63ae3e649112fcda19d293ba2fddcaf45674",
|
||||
[5] = "69ca46ebfae646b3a09faf9d636f3c706e5f6b7146f111a0ea06196332ae58b9",
|
||||
[6] = "307dc4b38ff341670a5b5179ab977b6c7c59ba682d529d80dcaf9c4852c21fe2",
|
||||
};
|
||||
|
||||
[Fact]
|
||||
|
|
@ -122,11 +122,11 @@ public class TrojanSwarmTests
|
|||
var a = swarmless.All.Where(x => x.Seed == seed).Select(x => x.Zone).ToList();
|
||||
var b = live.All.Where(x => x.Seed == seed).Select(x => x.Zone).ToList();
|
||||
Assert.Empty(a.SelectMany(z => z.AsteroidBelts.Where(x => x.Value.Data.RidesRail)));
|
||||
Assert.Equal(BaseDigest[seed], ZoneFingerprint.Of(a, _ => false, _ => false));
|
||||
string Swarmed(Zone z) => ZoneFingerprint.Of(new[] { z }, SwarmRails(z).Contains, IsSwarm);
|
||||
Assert.Equal(BaseDigest[seed], ZoneFingerprint.Of(a, _ => false, _ => false, true));
|
||||
string Swarmed(Zone z) => ZoneFingerprint.Of(new[] { z }, SwarmRails(z).Contains, IsSwarm, false);
|
||||
for (var n = 0; n < a.Count; n++)
|
||||
{
|
||||
Assert.Equal(ZoneFingerprint.Of(new[] { a[n] }, _ => false, _ => false), Swarmed(b[n]));
|
||||
Assert.Equal(ZoneFingerprint.Of(new[] { a[n] }, _ => false, _ => false, false), Swarmed(b[n]));
|
||||
swarms += SwarmRails(b[n]).Count;
|
||||
Assert.True(b[n].PopulatedRadius >= a[n].PopulatedRadius, $"seed {seed}: a swarm shrank the populated radius");
|
||||
if (b[n].PopulatedRadius > a[n].PopulatedRadius) grew++;
|
||||
|
|
|
|||
|
|
@ -9,29 +9,42 @@ using static CultMath.math;
|
|||
using float2 = CultMath.float2;
|
||||
|
||||
// A digest of what a zone's generation and build decide, so a test can pin it to a value computed at an earlier commit:
|
||||
// every orbit's (Distance, Phase, Period, Reach, Support), every body's (type, Mass, rail Distance, belt rocks), the entity
|
||||
// count and GetHeight at three points round each non-belt body. Cache keys and names are left out; they are minted, not decided.
|
||||
// every orbit's (Distance, Phase, Period, Reach, Support); every body's (type, Mass, rail Distance) and each belt rock's
|
||||
// (Distance, Phase, Size, RotationSpeed, Period); every orbital entity's (type, position, rail Distance and Phase) and any other
|
||||
// entity's type alone (a ship packed at the origin is scattered from the clock-seeded item stream, Zone.cs:276); the pack radius;
|
||||
// GetHeight on a 9x9 grid over the pack at t 0 and 777.7; and, when asked, PopulatedRadius. Cache keys and names are left
|
||||
// out; they are minted, not decided.
|
||||
public static class ZoneFingerprint
|
||||
{
|
||||
public static string Of(IEnumerable<Zone> zones, Func<CultRecordKey, bool> skipOrbit, Func<BodyData, bool> skipBody)
|
||||
public static string Of(IEnumerable<Zone> zones, Func<CultRecordKey, bool> skipOrbit, Func<BodyData, bool> skipBody, bool populatedRadius)
|
||||
{
|
||||
var text = new StringBuilder();
|
||||
foreach (var zone in zones) text.Append(Describe(zone, skipOrbit, skipBody)).Append('\n');
|
||||
foreach (var zone in zones) text.Append(Describe(zone, skipOrbit, skipBody, populatedRadius)).Append('\n');
|
||||
return Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(text.ToString()))).ToLowerInvariant();
|
||||
}
|
||||
|
||||
private static string Describe(Zone z, Func<CultRecordKey, bool> skipOrbit, Func<BodyData, bool> skipBody)
|
||||
private static string Describe(Zone z, Func<CultRecordKey, bool> skipOrbit, Func<BodyData, bool> skipBody, bool populatedRadius)
|
||||
{
|
||||
string F(float v) => v.ToString("R", System.Globalization.CultureInfo.InvariantCulture);
|
||||
string F(double v) => v.ToString("R", System.Globalization.CultureInfo.InvariantCulture);
|
||||
var orbits = z.Orbits.Where(o => !skipOrbit(o.Key))
|
||||
.Select(o => $"{F(o.Value.Data.Distance)},{F(o.Value.Data.Phase)},{F(o.Value.Period)},{F(z.Wells.Reach(o.Key))},{F(z.Wells.Support(o.Key))}")
|
||||
.OrderBy(x => x, StringComparer.Ordinal);
|
||||
var bodies = z.Planets.Values.Where(b => !skipBody(b))
|
||||
.Select(b => $"{b.GetType().Name},{F(b.Mass)},{F(z.Orbits[b.Orbit.Key].Data.Distance)}," +
|
||||
(b is AsteroidBeltData belt ? string.Join("|", belt.Asteroids.Select(r => $"{F(r.Distance)}/{F(r.Phase)}/{F(r.Size)}")) : ""))
|
||||
var bodies = z.Planets.Where(b => !skipBody(b.Value))
|
||||
.Select(b => $"{b.Value.GetType().Name},{F(b.Value.Mass)},{F(z.Orbits[b.Value.Orbit.Key].Data.Distance)}," +
|
||||
(b.Value is AsteroidBeltData belt ? string.Join("|", belt.Asteroids.Select((r, i) =>
|
||||
$"{F(r.Distance)}/{F(r.Phase)}/{F(r.Size)}/{F(r.RotationSpeed)}/{F(z.AsteroidBelts[b.Key].Periods[i])}")) : ""))
|
||||
.OrderBy(x => x, StringComparer.Ordinal);
|
||||
var heights = z.Planets.Values.Where(b => !(b is AsteroidBeltData)).Select(b => z.Orbits[b.Orbit.Key].Position)
|
||||
.SelectMany(p => new[] { float2(0, 0), float2(37, 0), float2(0, -211) }.Select(o => F(z.GetHeight(p + o))));
|
||||
return string.Join(";", orbits) + "#" + string.Join(";", bodies) + "#" + z.Entities.Count() + "#" + string.Join(",", heights);
|
||||
var entities = z.Entities.Select(e => e is OrbitalEntity o
|
||||
? $"{e.GetType().Name},{F(e.Position.x)},{F(e.Position.y)},{F(e.Position.z)},{F(z.Orbits[o.OrbitData].Data.Distance)},{F(z.Orbits[o.OrbitData].Data.Phase)}"
|
||||
: e.GetType().Name)
|
||||
.OrderBy(x => x, StringComparer.Ordinal);
|
||||
var r = z.Pack.Radius;
|
||||
var heights = new List<string>();
|
||||
foreach (var t in new[] { 0.0, 777.7 })
|
||||
for (var i = -4; i <= 4; i++)
|
||||
for (var j = -4; j <= 4; j++)
|
||||
heights.Add(F(z.GetHeight(float2(i * r / 4f, j * r / 4f), t)));
|
||||
return string.Join(";", orbits) + "#" + string.Join(";", bodies) + "#" + string.Join(";", entities) + "#" + F(r) + "#" +
|
||||
string.Join(",", heights) + (populatedRadius ? "#" + F(z.PopulatedRadius) : "");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue