Fauna r2: target content r2 (attacks, harvest, flow owner, Sol, seeded placement, kitchen-sink classes), map r2 (B15-B23, decisions, r2 cut sequence), placement prior art and probe
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
1746e8bdc8
commit
1e219e96e3
5 changed files with 595 additions and 21 deletions
|
|
@ -1,6 +1,6 @@
|
|||
# Aetheria spaceborne fauna: map
|
||||
|
||||
Proposed campaign `aetheria-fauna`. Target: `docs/aetheria-fauna-target.md` (draft r0). Written by Imagination
|
||||
Campaign `aetheria-fauna`. Target: `docs/aetheria-fauna-target.md` (content r2). Written by Imagination
|
||||
(agent imagination-aeth-fauna, session self-2026-10-13-ag2) against `origin/master` `0898725f`. This map holds the
|
||||
body facts, the questions Self admits together with the campaign, the first cut as a draft spec, and the rest of
|
||||
the cut sequence. Prior art: `docs/research/2026-10-13-fauna-prior-art.md` (`PA:<line>`). Items tagged `[M]` there
|
||||
|
|
@ -199,7 +199,7 @@ within a bounded radius.
|
|||
rocks unchanged, no per-tick member loop (grep `Zone.Update` and the step functions for any loop over
|
||||
`Asteroids`), and generation that is deterministic per zone.
|
||||
|
||||
## Cut sequence (one line each; each is mapped when it is about to run)
|
||||
## Cut sequence r1 (superseded by the r2 sequence below)
|
||||
|
||||
1. `fauna-krill-swarms`: krill fields through the chunk path, with the sway law and generation (drafted above).
|
||||
1b. `fauna-krill-catalog`: an AetherDb command that authors the Krill field kind (cross-section, sway, swarm weight).
|
||||
|
|
@ -221,3 +221,85 @@ rocks unchanged, no per-tick member loop (grep `Zone.Update` and the step functi
|
|||
term, no second one). Waits on 1, an operator yes.
|
||||
10. `orbit-sync-helper`: the generic flying aid that holds a ship on the local orbital flow. It is not fauna-specific
|
||||
and may belong to the flight campaign. Waits on an operator yes.
|
||||
|
||||
## r2: the flow, placement and the content the rulings opened
|
||||
|
||||
Written by Imagination (agent imagination-aeth-fauna-r2, session self-2026-10-13-ag2) against `origin/master`
|
||||
`1746e8bd`, after the operator ruled on the nine questions above. The questions above are answered; their rulings
|
||||
are in the mind. This section adds the body facts the flow ruling needs, the forks it opened, and the r2 cut
|
||||
sequence, which replaces the r1 sequence above. The krill draft above stays as the shape of the krill cut; it is
|
||||
re-mapped against the flow owner and the placement ruling when it is about to run.
|
||||
|
||||
### Body facts (r2)
|
||||
|
||||
Probed at `1746e8bd`.
|
||||
|
||||
| # | Question | Result |
|
||||
|---|---|---|
|
||||
| B15 | What is the flow? | `Volumetric.cginc`: `globalFlow(pos)` = `Tri3D(pos/_FlowScale - (0,_FlowScroll,0)) * _FlowAmplitude` with y halved (:130-135); `Tri3D` is the cross product of two normalised `tri3` folds, the second at the golden ratio (:46-47, :69-74); `slopeFlow(grad)` = `normalize(grad)` turned 90 degrees times `_FlowSwirlAmplitude` plus `normalize(grad)` times `_FlowSlopeAmplitude` (:137-141); `flow(pos)` sums the two under the keywords `FLOW_GLOBAL` and `FLOW_SLOPE` (:148-161). Consumers: `Stardust.compute:139-142` (each particle is placed at `-flow * lifetime * period`, a closed-form advection over a cycle) and the nebula's noise advection (`Volumetric.cginc:185-208`). |
|
||||
| B16 | Who owns it today? | The renderer. `VolumeSampling.Update` pushes the parameters from `environment.Flow` and accumulates `_flowScroll += GlobalScrollSpeed * Time.deltaTime` on the frame clock (`VolumeSampling.cs:96-106`). `environment` is `ActionGameManager.CurrentEnvironment`, which always returns `Settings.DefaultEnvironment` (`ActionGameManager.cs:222-228`), a field of the presentation `GameSettings` ScriptableObject (`GameSettings.cs:40`). `ZoneEnvironment` and `FlowSettings` are already `ServerShared` MessagePack types (`Environment.cs:8-61`), but no zone holds one. The sim cannot see the flow. |
|
||||
| B17 | What flow is live? | `Assets/Resources/Settings.asset:396-402` (LFS): `GlobalScale` 512, `GlobalAmplitude` 15, `GlobalScrollSpeed` 0.025, `Period` 8, `SlopeAmplitude` 0, `SwirlAmplitude` 0. Only the global term runs; `FLOW_SLOPE` is off in the shipped game. Probe: the horizontal global flow averages 6.06 u/s (max 12.2) across a zone, the same as the rail speed 2π = 6.28 u/s. |
|
||||
| B18 | Does the slope term have a sim twin? | It reads `_NebulaSurfaceHeight`, a GPU render of brush meshes (`Assets/Shaders/Brushes/*`), which is a second lowering of the heightfield `Zone.GetHeight` owns (`Zone.cs:627-660`). Its `getUV` negates world position (`Volumetric.cginc:81-84`), so its texel gradient points downhill in world terms. `normalize` of a zero gradient is NaN (:139); today the keyword keeps that from running. |
|
||||
| B19 | What clock do presenters use? | Zone time: `Zone._time` (double, `Zone.cs:70`). Belts are drawn at it (`ZoneRenderer.cs:543-547`), gas giant wave phase too (:587-588). `Zone.Settings` is the public `PlanetSettings` (`Zone.cs:59`), the world-law block every zone already receives. |
|
||||
| B20 | Placement prior art and probe | `docs/research/2026-10-13-fauna-placement-prior-art.md`. A CPU transliteration of the operator's `Slime.compute` with her prefab's parameters, fed by `Zone.GetNormal`: bit-identical run to run, 9 s for 40k agents × 600 steps on 256², trail on the shoulders of the wells and in filaments between them (0 % in flat space, 5.7 % in cores), correlation 0.28 with a closed-form slope-suitability map. Adding the flow reshapes the network (correlation 0.34 with the no-flow map). |
|
||||
| B21 | Is a generated zone recomputed? | No. A zone pack is generated once, on first entry (`ActionGameManager.cs:727`, `??=`; the entrance in `RunStart.cs:29`), and saved with the galaxy (`Galaxy.cs:70`). Placement computed at generation is saved; it never has to reproduce on another machine. |
|
||||
| B22 | Can Unity check a shader against C#? | Yes. `ShieldPanelCut3Verify.cs:18, 47-55` dispatches compute kernels and reads them back in batchmode run without `-nographics` (verified 2026-09-18), driven by `tests/mutation_tests_shield_panel_cut3.py`. That is the precedent for the flow parity check. Hands worktrees on Starfire have imported Libraries (`C:\aeth-wt\lock-on\Library` and others). |
|
||||
| B23 | Overlap with in-flight passes | ew-claims (Weapon.cs, Entity.cs, Sensing.cs, SchematicDisplay.cs, tests), scenarios-lock-on-r3 (Scenarios/LockOn.cs, Scenarios.cs, a test) and sensor-catalog-r2 (Behaviors, Listener.cs, Entity.cs, `GameData/Aetheria.cc`, AetherDb, tests) touch none of the flow cut's files. The flow cut edits `Settings.asset` (LFS); none of the three does. |
|
||||
|
||||
### Decisions made here, not forks
|
||||
|
||||
- **The scroll becomes zone time.** `sim-knows-the-flow` makes the flow a sim fact, and `fauna-pose-pure` needs it to
|
||||
be a function of zone time. `_FlowScroll` is therefore `GlobalScrollSpeed × Zone.Time`, derived, and the frame-clock
|
||||
accumulator is deleted. Where no zone exists (menus), the same function runs on the presentation clock; that is a
|
||||
different input to the one function, not a second law. `_FlowPeriod` and the noise phases on `_Time` stay
|
||||
presentation: they animate how the nebula shows the flow, not the flow.
|
||||
- **The flow settings live with the zone's world laws.** `FlowSettings` moves from `ZoneEnvironment` (read by no
|
||||
zone) to `PlanetSettings.Flow`, which every `Zone` already receives (B19). One zone flow for every zone, as today.
|
||||
Per-zone environments (Sol's cloudless bubble) are a later cut that adds a zone-pack override; the owner stays
|
||||
`Zone`.
|
||||
- **The slope term is ported and guarded.** It is part of the law the ruling names, so the owner carries it; its
|
||||
term is skipped when its amplitudes are zero (as the shader's keyword does), and a zero gradient gives zero, not
|
||||
NaN, on both sides. Its parity is pinned as a function of the gradient; making the heightfield's two lowerings
|
||||
agree is follow-up work (`flow-slope-gradient-parity`).
|
||||
|
||||
### Questions (admitted in the mind)
|
||||
|
||||
`aetheria-fauna:question:fauna-placement`, `:fauna-flow-coupling`, `:flow-moves-what`, `:flow-slope-terms`. The
|
||||
options and recommendations are in the mind; the research for the first is the placement prior-art file.
|
||||
|
||||
### Cut sequence (r2, one line each; each is mapped when it is about to run)
|
||||
|
||||
1. `fauna-flow-field` (cut spec admitted): the flow's owner moves into `ServerShared` (`FlowField`, `Zone.Flow`), the
|
||||
shader lowers it from zone time, and a Unity parity check pins HLSL to C#.
|
||||
2. `fauna-habitat-placement`: the generation pass per `fauna-placement`; its settings record in `ServerShared`;
|
||||
food at well shoulders and wormhole mouths; Sol excluded; budget measured. Waits on 1 and that ruling.
|
||||
3. `fauna-krill-swarms`: the krill draft above, re-mapped: krill fields placed by 2, swaying and drifting by
|
||||
`fauna-flow-coupling`. Waits on 1, 2. It cannot run beside 1: it needs `Zone.Flow`, and both edit `Zone.cs` and
|
||||
`Settings.cs`.
|
||||
4. `fauna-krill-catalog`: AetherDb authors the Krill field kind.
|
||||
5. `fauna-field-presentation`: fauna kinds pick their own look; `SectorRenderer` counts belts only.
|
||||
6. `fauna-minnow-schools`: minnows, fast milling, grazing the krill filaments.
|
||||
7. `fauna-anchors`: field frames that move across the flow under class laws (`fauna-motion-model-ruled`).
|
||||
8. `fauna-sharks-and-whales`: the two classes, generated by 2's map (sharks on steep flanks, whales round gas giants).
|
||||
9. `fauna-disturbance`: sparse reaction records; schools scatter, sharks veer; reactions are tells.
|
||||
10. `fauna-attacks-strike`: sharks strike and minnow schools scour, through the ordinary damage path.
|
||||
11. `fauna-attacks-signal`: whale song as noise in the shared signal space (with aetheria-ew), drifter discharge.
|
||||
12. `fauna-harvest-goods`: catalog goods per kind (krill paste, photophores, heat-organs, whale bone with strange
|
||||
matter, spider silk) and yields through the economy's yield table; fields refill by respawn.
|
||||
13. `fauna-harvest-gear`: resonance fishing, nets and trawls as gear with makers (the lore names Orbital Forge's
|
||||
retooled resonators); trawls leave straight-edged channels.
|
||||
14. `fauna-void-spiders`: nets strung across trojan points, snaring and tethering what drifts in; silk.
|
||||
15. `fauna-gas-giant-drifters`: a cloud-deck class bound to gas giants.
|
||||
16. `fauna-edge-giants`: larger classes found only far from Sol; the class mix scales with distance from Sol.
|
||||
17. `fauna-springs`: wormhole mouths feed placement and the refill tide.
|
||||
18. `fauna-genre-uses`: cultivators' harvesting techniques, necrotech-bound fauna, mecha that ride fauna; each lands
|
||||
with its owner's campaign, none trimmed for genre consistency.
|
||||
19. `sol-flatspace-zone`: a per-zone environment override; Sol is cloudless, flowless and fauna-free, with krill
|
||||
haze outside its Oort edge.
|
||||
20. `fauna-returns`: what an observer is shown (`fauna-distance-ruled`, `fauna-disguise-path-ruled`).
|
||||
21. `fauna-in-ew-blend`: fauna background kinds with motion-law envelopes in aetheria-ew's judge.
|
||||
22. `fauna-krill-obscure`: dense krill thin sight lines through `Zone.Obscuration`. Waits on an operator yes.
|
||||
23. `flow-moves-matter`: loose matter drifts with the flow, per `flow-moves-what`.
|
||||
24. `flow-slope-gradient-parity`: the brush-rendered heightfield and `Zone.GetHeight` agree, so the slope term can
|
||||
be switched on (`flow-slope-terms`).
|
||||
25. `orbit-sync-helper`: the generic flying aid that holds a ship on the local orbital flow. Waits on an operator yes.
|
||||
|
|
|
|||
|
|
@ -1,11 +1,20 @@
|
|||
# Aetheria spaceborne fauna: target
|
||||
|
||||
Proposed campaign `aetheria-fauna`, target draft r0 (Self admits the campaign and the target). Repo
|
||||
`GameCult/Aetheria`, working branch `master`, cut branches `eureka/aetheria-fauna-<cut>`. Written by Imagination
|
||||
(agent imagination-aeth-fauna, session self-2026-10-13-ag2) against `origin/master` `0898725f`. Body facts and
|
||||
probes are in `docs/aetheria-fauna-map.md` (cited below as `B<n>`); prior art is
|
||||
`docs/research/2026-10-13-fauna-prior-art.md` (cited as `PA:<line>`; items tagged `[M]` there are from memory and
|
||||
unverified). This is the target and the model page. The open forks are drafted as questions in the map.
|
||||
Campaign `aetheria-fauna`, target content r2 (r1 was admitted from the r0 draft at `f9705d32`; Self admits r2).
|
||||
Repo `GameCult/Aetheria`, working branch `master`, cut branches `eureka/aetheria-fauna-<cut>`. Written by
|
||||
Imagination (agents imagination-aeth-fauna and imagination-aeth-fauna-r2, session self-2026-10-13-ag2); r2 is
|
||||
against `origin/master` `1746e8bd`. Body facts and probes are in `docs/aetheria-fauna-map.md` (cited below as
|
||||
`B<n>`); prior art is `docs/research/2026-10-13-fauna-prior-art.md` (`PA:<line>`; `[M]` items are from memory and
|
||||
unverified) and `docs/research/2026-10-13-fauna-placement-prior-art.md` (placement). This is the target and the
|
||||
model page. Open forks are questions in the mind under `aetheria-fauna`.
|
||||
|
||||
r2 applies the operator's rulings of 2026-10-13: fauna attack ships in various ways (`fauna-interaction-ruled`),
|
||||
are harvestable and prominent in Elysium (`fauna-harvest-ruled`), are placed to tell an ecological story shaped by
|
||||
gravity (`fauna-habitat-ruled`), are native pseudospace life absent from Sol (`fauna-origin-pseudospace`,
|
||||
`sol-flatspace-bubble`), and drift with the global flow, whose authority moves into the sim (`sim-knows-the-flow`).
|
||||
The standing directions `aetheria-release:ruling:kitchen-sink-content`, `catalog-breadth-now` and
|
||||
`setting-permits-necrotech` make the ecology broad: more classes, more modes of harm, more goods, more uses, and no
|
||||
trimming for genre consistency.
|
||||
|
||||
## Purpose, in the operator's words
|
||||
|
||||
|
|
@ -26,13 +35,20 @@ data. Think the spacebound microfauna from the slime mold experiment". The index
|
|||
|
||||
## What fauna are for
|
||||
|
||||
Fauna are props: things in the sensor picture that are not ships, that a ship can be mistaken for, and that a
|
||||
Fauna are an ecology a pilot reads, hunts in, hides in, harvests and is hunted by. The first use is the oldest:
|
||||
they are props, things in the sensor picture that are not ships, that a ship can be mistaken for, and that a
|
||||
pilot must imitate by flying. They give the EW campaign (`docs/aetheria-ew-target.md`) a populated background. A
|
||||
hunter reading a crowded picture has to ask of each return whether it moves like what it seems to be. That is the
|
||||
prior art's oldest lesson: radar "angels" were birds and insects (PA:7). Insects drift with the wind and birds fly
|
||||
relative to it, so the motion tells them apart (PA:9). Sonar's false floor was a layer of animals that rose and fell
|
||||
with daylight (PA:14). Clutter that is alive is told apart by how it moves, not by how bright it is.
|
||||
|
||||
The other uses follow from the rulings. Fauna are a renewable harvest, big in Elysium, with goods for many trades
|
||||
(the lore draft names krill paste for Bloom concrete, photophores, shark heat-organs and whale bone carrying strange
|
||||
matter). Fauna are a hazard: classes attack ships, each in its own way. Fauna are material for every genre the
|
||||
factions carry: cultivators harvest them, necrotech binds them, mecha pilots ride them. Fauna are a map: where they
|
||||
gather shows the shape of the wells and the currents, and where they are missing says something happened.
|
||||
|
||||
## Design truths (ends, not means)
|
||||
|
||||
1. **Anything a sensor can perceive or a shot can hit is simulated; nothing below detection decides anything.**
|
||||
|
|
@ -61,6 +77,17 @@ with daylight (PA:14). Clutter that is alive is told apart by how it moves, not
|
|||
7. **The zone owns fauna; presenters read them.** No Unity callback writes fauna state. The renderer draws them as it
|
||||
draws belts, from a pose evaluated at zone time (`ZoneRenderer.cs:543-569`, B7).
|
||||
8. **The targeting index carries fauna as providers.** No consumer scans a swarm (`TargetingIndex.cs`, B4).
|
||||
9. **The flow is a world fact with one owner in the sim.** The global flow that stardust and nebula follow
|
||||
(`Volumetric.cginc` `flow()`, B15) is evaluated by one `ServerShared` function of position and zone time from the
|
||||
zone's flow settings. Fauna drift with it. The shader lowers the same function from the same settings and time,
|
||||
and a parity check pins the two. No presenter accumulates its own flow time (B16).
|
||||
10. **Placement tells an ecological story and is decided once.** The generator places fauna from the zone's gravity
|
||||
and flow, deterministically from the zone seed, and saves what it placed with the zone pack. Fauna gather on
|
||||
the shoulders of wells and along links between them, not in the cores and not in flat space (placement probe,
|
||||
`docs/research/2026-10-13-fauna-placement-prior-art.md`). The method is the question `fauna-placement`.
|
||||
11. **No fauna in Sol.** Sol is a single flatspace zone with no fauna inside it; the strangeness begins at its edge.
|
||||
12. **Harm and harvest go through existing owners.** A fauna attack is damage through the path a weapon's hit
|
||||
takes; a harvest yields items through the economy's yield path; neither gets a fauna-only damage or loot rule.
|
||||
|
||||
## Scale classes
|
||||
|
||||
|
|
@ -71,13 +98,19 @@ with daylight (PA:14). Clutter that is alive is told apart by how it moves, not
|
|||
| Shark | One to three members, each about the size of a ship | Reflection and moderate thermal | Crosses the flow; patrols with sprints | A ship-sized return a hunter must think about; the best disguise target for a fighter |
|
||||
| Whale | One member, many times a ship's size | Strong thermal; heard song (later) | Slow long arcs | A bright anchor; a ship near it is hard to separate (later: shadowing) |
|
||||
|
||||
The krill and minnow rows need nothing the Body lacks except a bounded per-kind motion term on field members (B3,
|
||||
B4). Sharks and whales need an anchor that does not ride the flow, which is the second cut onward.
|
||||
More classes are wanted, not fewer (`kitchen-sink-content`). The lore draft's room for more becomes classes in the
|
||||
cut sequence: *void spiders* that string nets across trojan points and snare what drifts in, *gas giant drifters*
|
||||
that live in the upper cloud deck, and *edge giants*, the larger things past the charts, found only far from Sol.
|
||||
Each class gets its own motion law, its own mode of harm and its own goods. Attack modes are designed per class in
|
||||
later cuts; the ones the lore implies are sharks that strike, minnow schools that scour sensors and hulls, whale
|
||||
song that is noise in the shared signal space, spider nets that snare and tether, and drifters that discharge.
|
||||
|
||||
The krill and minnow rows need nothing the Body lacks except a bounded per-kind motion term on field members and the
|
||||
flow (B3, B4, B15). Sharks and whales need an anchor that does not ride the flow, which comes later.
|
||||
|
||||
## Model page: identity, lifecycle, authority
|
||||
|
||||
This assumes the recommended options of the questions in the map (`fauna-in-sim`, `fauna-identity`,
|
||||
`fauna-motion-model`). A different ruling rewrites these rows.
|
||||
The rows follow the rulings `fauna-in-sim-ruled`, `fauna-identity-ruled` and `fauna-motion-model-ruled`.
|
||||
|
||||
| Kind | Identity (what names it) | Lifecycle | Authority (who decides; forbidden writers) |
|
||||
|---|---|---|---|
|
||||
|
|
@ -88,6 +121,10 @@ This assumes the recommended options of the questions in the map (`fauna-in-sim`
|
|||
| **Fauna detection** (derived) | `(observer, member)`, never stored. | Derived per query: `ChunkInfo` → `PassiveInfo` → `Sensor.Gain`. | `Entity`. Forbidden: any fauna-specific threshold or gain. |
|
||||
| **Swarm anchor** (later, sharks and whales) | Per field: a law that moves the field's frame across the flow. | Pure function of zone time plus sparse event records (fork `fauna-motion-model`). | `Zone`. Forbidden: per-member stepping. |
|
||||
| **Disturbance** (later) | `(member, time, push)`, sparse, as `ChunkWear` is. | Written when a hull passes through or a shot lands, then decays lazily against zone time. | `Zone`. It is read only by `Pose`. |
|
||||
| **Flow field** (new owner) | One law: `FlowField`, evaluated per zone by `Zone.Flow(position)` from the zone's world-law settings (`PlanetSettings.Flow`). | Settings authored in `Settings.asset`; time is zone time. Never stored per position. | `Zone`. Derived: every shader flow uniform (`_FlowScroll` and the rest). Forbidden: `VolumeSampling._flowScroll`, `ZoneEnvironment.Flow`, any frame-time accumulator. |
|
||||
| **Habitat placement** (generation intermediate) | None: it exists only inside `ZoneGenerator.GenerateZone`. | Computed once per zone from the seed, gravity and flow; consumed to place fields; discarded. | `ZoneGenerator`. Its products, fauna fields, are saved as belts are. Forbidden: recomputing on load, saving the intermediate map. |
|
||||
| **Attack** (later, per class) | `(member or swarm, ship, time)`, an event. | Raised by a class law when its trigger holds; applied through the ordinary damage path. | `Zone`. Forbidden: a fauna-only damage rule. |
|
||||
| **Harvest yield** (later) | Item kinds in the catalog, per fauna kind. | A member broken or netted by harvesting gear yields items through the economy's yield table; the field refills by respawn. | The catalog and the yield table. Forbidden: fauna-only loot code. |
|
||||
| **Fauna background kind** (aetheria-ew) | Entries of aetheria-ew's `BackgroundKind` for fauna classes (that target's row says "more by ruling"). | Static data. | `GameplaySettings`, per aetheria-ew. Its envelope gains a motion term per class (fork `fauna-disguise-path`). |
|
||||
|
||||
## Invariants
|
||||
|
|
@ -99,19 +136,31 @@ This assumes the recommended options of the questions in the map (`fauna-in-sim`
|
|||
- `fauna-cost-per-swarm`: zone update cost does not grow with member count. This is pinned with the index's
|
||||
`Examined` counter and by `Zone.Update` touching no member, not by timing.
|
||||
- `fauna-one-gain`: a fauna member is visible exactly when `ChunkVisible` says so.
|
||||
- `fauna-bounds-hold`: every member of a swaying field lies inside the regions its provider offers.
|
||||
- `fauna-tell-holds`: a krill cloud's centroid velocity equals the orbital flow at its distance, within the sway
|
||||
bound. Each later class gets its own tell test.
|
||||
- `fauna-bounds-hold`: every member of a swaying or drifting field lies inside the regions its provider offers.
|
||||
- `fauna-tell-holds`: each class's members move by that class's law; a krill cloud moves with the orbital flow and
|
||||
the global flow, within its sway bound. Each class gets its own tell test.
|
||||
- `fauna-never-lie`: no claim, suppression or belief is written for a fauna.
|
||||
- `flow-one-owner`: the global flow has one owner, `FlowField` in `ServerShared`, evaluated at zone time from the
|
||||
zone's flow settings. Every shader flow uniform is derived from it; no presenter keeps its own flow clock.
|
||||
- `flow-shader-parity`: the HLSL `globalFlow` and `slopeFlow` in `Volumetric.cginc` give the C# owner's values for
|
||||
the same position, time, settings and gradient, within a stated tolerance; a parity check that evaluates both
|
||||
pins it and fails when either side changes alone.
|
||||
- `flow-step-free`: the flow at a position and zone time does not depend on step size or frame rate.
|
||||
- `fauna-none-in-sol`: Sol's zone holds no fauna field; generation never places one there.
|
||||
- `fauna-placement-seeded`: a zone's fauna placement is a function of its seed and its generated bodies on the
|
||||
generating runtime; the saved zone pack is the authority after that, and loading never recomputes placement.
|
||||
|
||||
## Not in scope
|
||||
|
||||
- Ambient presentation-only life (the slime trails) beyond leaving it untouched.
|
||||
- Fauna AI that attacks ships, unless `fauna-interaction` rules otherwise.
|
||||
- Harvest economies and yield tables for fauna (the mining yield table is aetheria-release's).
|
||||
- Ambient presentation-only life (the slime trails) beyond leaving it untouched; its rules may be reused at
|
||||
generation, but the presenter stays presentation.
|
||||
- Phantom positions (aetheria-ew `ew-where-lies-ruled`).
|
||||
- Any lore claim about what the fauna are. No fauna lore was found in `Vault` or AetheriaLore (B11), so the
|
||||
operator writes it, or rules that Imagination drafts it.
|
||||
- Ships, floating items, mines and clouds drifting with the flow, until `flow-moves-what` is ruled.
|
||||
- Per-zone flow and nebula environments; Sol's cloudless bubble is a later cut, and until it lands every zone
|
||||
shares one flow.
|
||||
|
||||
Attacks and harvest are in scope as later cuts, designed per class (`fauna-interaction-ruled`,
|
||||
`fauna-harvest-ruled`); the first cuts build the ground they stand on: the flow, placement and the krill field.
|
||||
|
||||
## Cut sequence
|
||||
|
||||
|
|
|
|||
92
docs/research/2026-10-13-fauna-placement-prior-art.md
Normal file
92
docs/research/2026-10-13-fauna-placement-prior-art.md
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
# Fauna placement: prior art and a probe
|
||||
|
||||
Imagination, agent `imagination-aeth-fauna-r2`, session self-2026-10-13-ag2, for campaign `aetheria-fauna`. It
|
||||
answers one question: how should the zone generator decide where fauna live, so that placement "tells an ecological
|
||||
story ... an ecology shaped by the level" (ruling `fauna-habitat-ruled`)? Self proposed a seeded CPU Physarum pass
|
||||
at zone generation, with closed-form gradient placement as the alternative. Citations were checked on the web on
|
||||
2026-10-13 unless marked `[M]` (from memory, unverified).
|
||||
|
||||
## Sources
|
||||
|
||||
1. **Jones 2010.** J. Jones, "Characteristics of pattern formation and evolution in approximations of Physarum
|
||||
transport networks", *Artificial Life* 16(2):127-153, 2010, doi 10.1162/artl.2010.16.2.16202 (UWE repository
|
||||
record https://uwe-repository.worktribe.com/output/980579). Particle agents sense a trail map with three sensors,
|
||||
turn toward the strongest, move and deposit; the map diffuses and decays. Parameter maps give characteristic
|
||||
patterns. Self's citation is correct.
|
||||
2. **Jones 2011.** "Influences on the formation and evolution of Physarum polycephalum inspired emergent transport
|
||||
networks", *Natural Computing* 10(4):1345-1369, doi 10.1007/s11047-010-9223-z. The same model approximates Steiner
|
||||
trees, relative neighbourhood graphs and convex and concave hulls of its food sources. This matters: the network a
|
||||
Physarum pass draws between wells is a cheap Steiner-like graph linking them.
|
||||
3. **Tero et al. 2010.** "Rules for biologically inspired adaptive network design", *Science* 327(5964):439-442,
|
||||
doi 10.1126/science.1177894. The organism, with food at the Tokyo-area cities, grew a network comparable in cost,
|
||||
efficiency and fault tolerance to the rail system; a flow-reinforcement model reproduces it.
|
||||
4. **Monte Carlo Physarum Machine.** Burchett et al., ApJ Letters 2020, and Elek et al., arXiv 2204.01256 (2022):
|
||||
Jones's agent model, made probabilistic and 3D, fed galaxy positions and masses, reconstructs the cosmic web's
|
||||
filaments. Polyphorm (IEEE VIS 2020) is the interactive tool. This is the closest prior art to the operator's
|
||||
experiment: a slime-mould agent model fed by gravitating masses draws filaments between them.
|
||||
5. **EcoBrush.** Gain, Long, Cordonnier and Cani, *Computer Graphics Forum* 36(2):63-73, 2017, doi
|
||||
10.1111/cgf.13107. Ecosystem simulations are run offline, their plant distributions are stored in a database
|
||||
indexed by terrain conditions (slope, light, moisture, temperature), and a terrain is populated by lookup and
|
||||
interpolation, much faster than simulating it. This is the "simulate once, distil into a closed form" hybrid.
|
||||
6. **Cordonnier et al. 2017.** "Authoring landscapes by combining ecosystem and terrain erosion simulation", *ACM
|
||||
ToG* 36(4), doi 10.1145/3072959.3073667. Vegetation and erosion simulations feed each other; placement is the
|
||||
outcome of a coupled simulation, not a rule.
|
||||
7. **D8 flow accumulation.** O'Callaghan and Mark 1984, *Computer Vision, Graphics and Image Processing* 28:323-344.
|
||||
Each cell drains to its steepest neighbour, and accumulated upstream area marks channels. It is the standard
|
||||
closed-form way to derive "where things collect" from a heightfield. On a gravity heightfield it collects into
|
||||
well cores, which is where the lore says fauna are torn apart, so it answers the wrong question here.
|
||||
8. **Maxent.** Phillips, Anderson and Schapire 2006, *Ecological Modelling* 190:231-259. Species distribution
|
||||
models fit habitat suitability as a function of environmental covariates. It is the closed-form reading of
|
||||
habitat: suitability is a function of local conditions, with no network between patches.
|
||||
9. **Physarum parameters.** The programmable Physarum machines paper (arXiv 0901.4556) reports that the ratio of
|
||||
rotation angle to sensor angle tunes the network from contracting to branching. The operator's prefab varies
|
||||
both with slope, so the network contracts on flat ground and branches on slopes.
|
||||
10. **Float determinism.** Transcendentals (sin, cos, acos) can differ across platforms and runtimes (Factorio's
|
||||
developers hit it; Unity's forum reports x86 versus ARM physics divergence). A chaotic agent model amplifies a
|
||||
last-bit difference into a different map. A placement computed this way must be computed once and saved, never
|
||||
recomputed on another machine.
|
||||
|
||||
## Probe (real code, Yggdrasil)
|
||||
|
||||
A scratch xunit test (`2026-10-13-fauna-placement-probe/FaunaPlacementProbeScratch.cs.txt`, never merged) at
|
||||
`1746e8bd`, run on Yggdrasil (dotnet sdk 10, Release, 4 CPUs; 1 test passed; output in
|
||||
`2026-10-13-fauna-placement-probe/output.txt`). The zone is synthetic: radius 2000, a p50 sun at the centre, two
|
||||
p50 planets, a p50 gas giant with a moon, a planetoid, built with the real `Zone`, the real `PlanetSettings` values
|
||||
and `Zone.GetNormal`. The agent rules are the operator's `Slime.compute` (`UpdateAgents`, `Diffuse`) transliterated
|
||||
to C#, with her `Slime Renderer.prefab` parameter values, 40,000 agents, 600 steps of 0.1 s, a 256² trail map, and
|
||||
a hash RNG.
|
||||
|
||||
| Measure | Result |
|
||||
|---|---|
|
||||
| Cost | 8.6 to 9.4 s single-threaded for 40k agents × 600 steps on a 256² map. Agent-steps dominate; 10k × 300 on 128² should be about 1 s (extrapolated, not run). The slope grid (65k `GetNormal` calls) took 0.2 s. |
|
||||
| Run-to-run determinism | Two runs: identical FNV hash of the trail map (bit-for-bit). Across platforms: not tested, and not expected (source 10). |
|
||||
| Difference from closed form | Correlation of the trail map with a closed-form suitability map (her `Deposition(slope)`) is 0.28, and 0.29 against that map blurred. The Physarum pass is not a smoothed slope map: it draws filaments. |
|
||||
| Where the trail lies | By `u` = distance to the nearest well over its support radius: core (`u` < 0.1) 5.7 %, 0.1 to 0.3 35.6 %, 0.3 to 0.6 54.1 %, 0.6 to 1 4.7 %, outside every well 0 %. The trail sits on the shoulders of the wells, avoids the cores and links wells by filaments. This is the lore's "bloom on the shoulder of each well ... filaments that link one body's cloud to the next" without anyone writing it. |
|
||||
| Effect of the flow | Adding the live global flow to agent motion changes the map completely (correlation with the no-flow map 0.34). |
|
||||
| The live flow's speed | `Settings.asset` has `GlobalScale` 512, `GlobalAmplitude` 15 and `SlopeAmplitude` = `SwirlAmplitude` = 0. The horizontal global flow averages 6.06 u/s (max 12.2) over the zone, the same as the rail speed 2π = 6.28 u/s. Anything that drifts with the flow moves as fast as orbits do. |
|
||||
|
||||
The ASCII maps in `output.txt` show the slope field, the closed-form map and both Physarum maps.
|
||||
|
||||
## Reading
|
||||
|
||||
- The operator's experiment is the cosmic-web method (source 4) in miniature. What she liked, patterns that link
|
||||
the wells, is what a Physarum pass does and a local suitability rule cannot: a suitability function of local
|
||||
slope (Maxent's shape, source 8) has no links between patches. The probe's 0.28 correlation measures the gap.
|
||||
- A closed form can draw the links if it is told to: Jones 2011 says the network approximates a Steiner tree or a
|
||||
relative neighbourhood graph of the food sources, so filaments along the RNG of the zone's bodies and wormhole
|
||||
mouths, plus a shoulder band per well, is a deterministic closed-form imitation. It gives straight, legible links
|
||||
and loses the organic branching.
|
||||
- EcoBrush's hybrid (simulate offline, distil into a lookup by local conditions) keeps the cost low but, like any
|
||||
local lookup, cannot reproduce zone-specific topology.
|
||||
- The cost (seconds at the probed size) and the chaos (source 10) are both answered by running the pass once, when
|
||||
the zone is generated, and saving what it produced: the swarm fields, as belts are saved in the zone pack. The
|
||||
trail map is a generation intermediate, never state.
|
||||
|
||||
## Recommendation for the placement question
|
||||
|
||||
`physarum-at-generation`: run the operator's rules (her parameters, moved into a `ServerShared` settings record)
|
||||
once in `ZoneGenerator`, fed by `Zone.GetNormal` and the zone's flow field, seeded from the zone seed, with food
|
||||
sources at each well's shoulder and at wormhole mouths (the lore's springs); sample its trail map to place and
|
||||
shape fauna fields; save the fields; discard the map. Budget it to about a second (fewer agents and steps, a 128²
|
||||
map) and measure that in the cut. Confidence: moderate-high for the look (the probe shows the filaments and the
|
||||
shoulder band); moderate for the cost, which is extrapolated below the probed size.
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using CultMath;
|
||||
using GameCult.Caching;
|
||||
using Xunit;
|
||||
using static CultMath.math;
|
||||
using float2 = CultMath.float2;
|
||||
using float3 = CultMath.float3;
|
||||
|
||||
// SCRATCH placement probe (Imagination, agent imagination-aeth-fauna-r2). Never merged.
|
||||
// Q1: what does a CPU Physarum pass (the operator's Slime.compute rules, her prefab's parameters) fed by Zone.GetNormal
|
||||
// produce on a real heightfield, at what cost, and is it bit-deterministic run to run?
|
||||
// Q2: how different is its trail map from a closed-form slope-suitability map (corr)?
|
||||
// Q3: how fast is the live global flow (Settings.asset: scale 512, amplitude 15) against the rail speed 2*pi?
|
||||
public sealed class FaunaPlacementProbeScratch : IDisposable
|
||||
{
|
||||
private readonly string _root = Path.Combine(Path.GetTempPath(), "faunaprobe-" + Guid.NewGuid().ToString("N"));
|
||||
private readonly List<CultCache> _caches = new List<CultCache>();
|
||||
private readonly StringBuilder _o = new StringBuilder();
|
||||
public FaunaPlacementProbeScratch() { Directory.CreateDirectory(_root); Directory.CreateDirectory("/tmp/probe"); }
|
||||
public void Dispose() { foreach (var c in _caches) c.Dispose(); Directory.Delete(_root, true); }
|
||||
|
||||
private static PlanetSettings Real() => new PlanetSettings
|
||||
{
|
||||
ZoneDepthExponent = 2, ZoneDepth = 64, ZoneBoundaryFog = 0,
|
||||
GravityDepth = new ExponentialCurve { Exponent = .175f, Multiplier = 30, Constant = 0 },
|
||||
GravityRadius = new ExponentialCurve { Exponent = .25f, Multiplier = 500, Constant = 0 },
|
||||
WaveDepth = new ExponentialCurve { Exponent = .3f, Multiplier = .5f, Constant = 0 },
|
||||
WaveRadius = new ExponentialCurve { Exponent = .25f, Multiplier = 500, Constant = 0 },
|
||||
WaveFrequency = new ExponentialCurve { Exponent = .45f, Multiplier = .2f, Constant = 0 },
|
||||
WaveSpeed = new ExponentialCurve { Exponent = 0, Multiplier = 0, Constant = .025f },
|
||||
LightRadius = new ExponentialCurve { Exponent = .25f, Multiplier = 300, Constant = 0 },
|
||||
BodyRadius = new ExponentialCurve { Exponent = .25f, Multiplier = 3, Constant = 0 },
|
||||
GravityStrength = 2,
|
||||
OrbitPeriod = new ExponentialCurve { Exponent = 1, Multiplier = 1, Constant = 0 },
|
||||
};
|
||||
|
||||
private Zone NewZone(float packRadius, (string type, float mass, float dist, float phase)[] bodies)
|
||||
{
|
||||
var cache = AetheriaStores.Open(Path.Combine(_root, Guid.NewGuid().ToString("N") + ".cc"), runPath: Path.Combine(_root, Guid.NewGuid().ToString("N") + ".run.cc"), catalogWritable: true);
|
||||
_caches.Add(cache);
|
||||
var gs = new GameplaySettings
|
||||
{
|
||||
DefaultEntitySettings = new EntitySettings(),
|
||||
Tiers = new[] { new RarityTier { Name = "Common", Quality = .5f, Rarity = 0, Color = new float3(1, 1, 1) } },
|
||||
QualityPriceModifier = new ExponentialLerp()
|
||||
};
|
||||
var items = new ItemManager(cache, new ProvenanceLedger(), gs, _ => { });
|
||||
var pack = new ZonePack { Radius = packRadius };
|
||||
foreach (var (type, mass, dist, phase) in bodies)
|
||||
{
|
||||
BodyData b = type == "Sun" ? new SunData() : type == "GasGiant" ? new GasGiantData() : (BodyData) new PlanetData();
|
||||
b.Mass = mass;
|
||||
var orbit = new OrbitData { Distance = 0, FixedPosition = new float2(cos(phase), sin(phase)) * dist };
|
||||
cache.Upsert(orbit);
|
||||
b.Orbit = cache.RefOf(orbit);
|
||||
cache.Upsert(b);
|
||||
pack.Orbits.Add(cache.RefOf(orbit));
|
||||
pack.Planets.Add(cache.RefOf(b));
|
||||
}
|
||||
var zone = new Zone(items, Real(), pack, new GalaxyZone { Name = "Probe", Owner = null }, null);
|
||||
zone.Update(1f / 60f);
|
||||
return zone;
|
||||
}
|
||||
|
||||
// Volumetric.cginc globalFlow, transliterated for the probe only.
|
||||
private static float tri(float x) => abs(frac(x) - .5f);
|
||||
private static float3 tri3(float3 p) => new float3(tri(p.z + tri(p.y)), tri(p.z + tri(p.x)), tri(p.y + tri(p.x)));
|
||||
private static float3 Tri3D(float3 p) => cross(normalize(tri3(p)), normalize(tri3(p * 1.61803398875f)));
|
||||
private static float3 GlobalFlow(float3 pos, float scroll, float scale = 512, float amp = 15)
|
||||
{
|
||||
var f = Tri3D(pos / scale - new float3(0, scroll, 0)) * amp;
|
||||
f.y *= .5f;
|
||||
return f;
|
||||
}
|
||||
|
||||
private struct P { public float Thr, Blend, Hi, Lo, Exp; public P(float t, float b, float h, float l, float e) { Thr = t; Blend = b; Hi = h; Lo = l; Exp = e; } }
|
||||
private static float Eval(P p, float s) => lerp(p.Lo, p.Hi, pow(smoothstep(p.Thr - p.Blend, p.Thr + p.Blend, s), p.Exp));
|
||||
// Slime Renderer.prefab values.
|
||||
private static readonly P Deposition = new P(.3f, .3f, 2, 0, .8f), Diffusion = new P(.3f, .3f, 1, .5f, 1), Decay = new P(.5f, .5f, 20, .5f, 1),
|
||||
Turn = new P(.5f, .5f, 128, 4, 2), Speed = new P(.5f, .5f, 0, 20, 1), Drive = new P(.5f, .4f, 64, 4, 1),
|
||||
SensorDistance = new P(.5f, .5f, 8, 64, .125f), SensorSpread = new P(.4f, .4f, .1f, 2, .5f);
|
||||
|
||||
private static uint Hash(uint s) { s ^= 2747636419u; s *= 2654435769u; s ^= s >> 16; s *= 2654435769u; s ^= s >> 16; s *= 2654435769u; return s; }
|
||||
private static float Rnd(uint s) => Hash(s) / 4294967295f;
|
||||
|
||||
private float[] Physarum(Zone zone, float R, int G, int agents, int steps, float dt, bool flow, float[] slope, out long ms)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
var trail = new float[G * G];
|
||||
var next = new float[G * G];
|
||||
var pos = new float2[agents];
|
||||
var ang = new float[agents];
|
||||
for (var i = 0; i < agents; i++)
|
||||
{
|
||||
var r = R * sqrt(Rnd((uint) i * 3u)); var a = 2 * PI * Rnd((uint) i * 3u + 1);
|
||||
pos[i] = new float2(cos(a), sin(a)) * r; ang[i] = 2 * PI * Rnd((uint) i * 3u + 2);
|
||||
}
|
||||
int Cell(float2 p) { var u = (p / (2 * R) + .5f) * G; var x = clamp((int) u.x, 0, G - 1); var y = clamp((int) u.y, 0, G - 1); return y * G + x; }
|
||||
float Sense(float2 p, float a, float d) { var c = (p / (2 * R) + .5f) * G + new float2(cos(a), sin(a)) * d; var s = 0f;
|
||||
for (var dx = -1; dx <= 1; dx++) for (var dy = -1; dy <= 1; dy++) { var x = (int) c.x + dx; var y = (int) c.y + dy; if (x >= 0 && x < G && y >= 0 && y < G) s += trail[y * G + x]; } return s; }
|
||||
for (var t = 0; t < steps; t++)
|
||||
{
|
||||
for (var i = 0; i < agents; i++)
|
||||
{
|
||||
var c = Cell(pos[i]); var s = slope[c];
|
||||
pos[i] += new float2(cos(ang[i]), sin(ang[i])) * Eval(Speed, s) * dt;
|
||||
if (flow) { var f = GlobalFlow(new float3(pos[i].x, 0, pos[i].y), .025f * t * dt); pos[i] += new float2(f.x, f.z) * dt; }
|
||||
var rr = Rnd((uint) (t * agents + i) * 7u);
|
||||
if (lengthsq(pos[i]) > R * R || float.IsNaN(pos[i].x) || s > .8f)
|
||||
{
|
||||
var r = R * sqrt(Rnd((uint) (t * agents + i) * 7u + 3)); var a = 2 * PI * Rnd((uint) (t * agents + i) * 7u + 4);
|
||||
pos[i] = new float2(cos(a), sin(a)) * r; ang[i] = rr * 2 * PI;
|
||||
}
|
||||
c = Cell(pos[i]); s = slope[c];
|
||||
var sp = Eval(SensorSpread, s); var d = Eval(SensorDistance, s);
|
||||
var fwd = Sense(pos[i], ang[i], d); var l = Sense(pos[i], ang[i] - sp, d); var rt = Sense(pos[i], ang[i] + sp, d);
|
||||
var mn = min(min(l, rt), fwd); var mx = max(max(max(l, rt), fwd), .01f); var range = min(mx - mn, .01f);
|
||||
var ln = (l - mn) / (mx - mn); var rn = (rt - mn) / (mx - mn);
|
||||
if (float.IsNaN(ln)) ln = 0; if (float.IsNaN(rn)) rn = 0;
|
||||
var motivation = pow(range / mx, 1 / max(Eval(Drive, s), .001f));
|
||||
ang[i] += Eval(Turn, s) * lerp(rr * 2 - 1, ln - rn, motivation) * dt;
|
||||
trail[c] += Eval(Deposition, s) * dt;
|
||||
}
|
||||
for (var y = 0; y < G; y++) for (var x = 0; x < G; x++)
|
||||
{
|
||||
var sum = 0f; for (var dx = -1; dx <= 1; dx++) for (var dy = -1; dy <= 1; dy++) { var xx = x + dx; var yy = y + dy; if (xx >= 0 && xx < G && yy >= 0 && yy < G) sum += trail[yy * G + xx]; }
|
||||
var s = slope[y * G + x];
|
||||
next[y * G + x] = lerp(trail[y * G + x], sum / 9, saturate(Eval(Diffusion, s) * dt)) * saturate(1 - Eval(Decay, s) * dt);
|
||||
}
|
||||
(trail, next) = (next, trail);
|
||||
}
|
||||
ms = sw.ElapsedMilliseconds;
|
||||
return trail;
|
||||
}
|
||||
|
||||
private static ulong Fnv(float[] a) { ulong h = 1469598103934665603UL; foreach (var v in a) { var b = BitConverter.SingleToInt32Bits(v); for (var k = 0; k < 4; k++) { h ^= (byte) (b >> (8 * k)); h *= 1099511628211UL; } } return h; }
|
||||
private static double Corr(float[] a, float[] b) { double ma = 0, mb = 0; for (var i = 0; i < a.Length; i++) { ma += a[i]; mb += b[i]; } ma /= a.Length; mb /= a.Length;
|
||||
double sab = 0, saa = 0, sbb = 0; for (var i = 0; i < a.Length; i++) { var x = a[i] - ma; var y = b[i] - mb; sab += x * y; saa += x * x; sbb += y * y; } return sab / Math.Sqrt(saa * sbb); }
|
||||
private void Ascii(string name, float[] m, int G, bool log)
|
||||
{
|
||||
const string ramp = " .:-=+*#%@"; var W = 64; var H = 32; var cell = new float[W * H]; var max = 1e-9f;
|
||||
for (var y = 0; y < G; y++) for (var x = 0; x < G; x++) { var i = (y * H / G) * W + x * W / G; cell[i] += m[y * G + x]; }
|
||||
for (var i = 0; i < cell.Length; i++) { if (log) cell[i] = (float) Math.Log(1 + cell[i]); max = Math.Max(max, cell[i]); }
|
||||
_o.AppendLine("@@MAP " + name);
|
||||
for (var y = H - 1; y >= 0; y--) { var sb = new StringBuilder("|"); for (var x = 0; x < W; x++) sb.Append(ramp[Math.Min(9, (int) (cell[y * W + x] / max * 9.99f))]); _o.AppendLine(sb.Append('|').ToString()); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Probe()
|
||||
{
|
||||
var inv = CultureInfo.InvariantCulture;
|
||||
// A sun at the centre, two planets, a gas giant with a moon, a planetoid: p50 masses from the orbit probe.
|
||||
const float R = 2000;
|
||||
var zone = NewZone(R, new[] { ("Sun", 14152f, 0f, 0f), ("Planet", 274f, 610f, .4f), ("Planet", 274f, 1050f, 2.6f),
|
||||
("GasGiant", 2260f, 1400f, 4.4f), ("Planet", 120f, 1400f + 300f, 4.6f), ("Planet", 29f, 450f, 3.9f) });
|
||||
const int G = 256;
|
||||
var slope = new float[G * G]; var suit = new float[G * G]; var flowMag = new double[3];
|
||||
var sw = Stopwatch.StartNew();
|
||||
for (var y = 0; y < G; y++) for (var x = 0; x < G; x++)
|
||||
{
|
||||
var p = (new float2(x + .5f, y + .5f) / G - .5f) * 2 * R;
|
||||
var n = zone.GetNormal(p);
|
||||
var s = lengthsq(p) > R * R ? 0f : acos(clamp(n.y, -1, 1)) / (PI / 2);
|
||||
slope[y * G + x] = s;
|
||||
suit[y * G + x] = lengthsq(p) > R * R ? 0 : Eval(Deposition, s) * (s > .8f ? 0 : 1);
|
||||
}
|
||||
_o.AppendLine($"slope-grid-ms {sw.ElapsedMilliseconds}");
|
||||
// Q3: live global flow magnitude over the zone at three scroll values.
|
||||
double sumH = 0, maxH = 0, sumY = 0; var n3 = 0;
|
||||
foreach (var scroll in new[] { 0f, 1f, 7.3f })
|
||||
for (var y = 0; y < 64; y++) for (var x = 0; x < 64; x++)
|
||||
{
|
||||
var p = (new float2(x + .5f, y + .5f) / 64 - .5f) * 2 * R;
|
||||
var f = GlobalFlow(new float3(p.x, 0, p.y), scroll);
|
||||
var h = length(new float2(f.x, f.z)); sumH += h; maxH = Math.Max(maxH, h); sumY += abs(f.y); n3++;
|
||||
}
|
||||
_o.AppendLine(string.Format(inv, "globalflow horizontal mean {0:F3} max {1:F3} |y| mean {2:F3} u/s; rail speed {3:F3}", sumH / n3, maxH, sumY / n3, 2 * PI));
|
||||
|
||||
var a1 = Physarum(zone, R, G, 40000, 600, .1f, false, slope, out var msA1);
|
||||
var a2 = Physarum(zone, R, G, 40000, 600, .1f, false, slope, out var msA2);
|
||||
var b1 = Physarum(zone, R, G, 40000, 600, .1f, true, slope, out var msB1);
|
||||
_o.AppendLine($"physarum G=256 agents=40000 steps=600 ms A1 {msA1} A2 {msA2} B(flow) {msB1}");
|
||||
_o.AppendLine($"hash A1 {Fnv(a1):x16} A2 {Fnv(a2):x16} equal {Fnv(a1) == Fnv(a2)}");
|
||||
_o.AppendLine(string.Format(inv, "corr(A, closed-form suitability) {0:F3}; corr(B, suitability) {1:F3}; corr(A, B) {2:F3}", Corr(a1, suit), Corr(b1, suit), Corr(a1, b1)));
|
||||
// Blurred-suitability correlation: does Physarum add structure beyond a smoothed slope map?
|
||||
var blur = (float[]) suit.Clone(); var tmp = new float[G * G];
|
||||
for (var k = 0; k < 6; k++) { for (var y = 0; y < G; y++) for (var x = 0; x < G; x++) { var s = 0f; var c = 0; for (var dx = -2; dx <= 2; dx++) for (var dy = -2; dy <= 2; dy++) { var xx = x + dx; var yy = y + dy; if (xx >= 0 && xx < G && yy >= 0 && yy < G) { s += blur[yy * G + xx]; c++; } } tmp[y * G + x] = s / c; } (blur, tmp) = (tmp, blur); }
|
||||
_o.AppendLine(string.Format(inv, "corr(A, blurred suitability) {0:F3}", Corr(a1, blur)));
|
||||
// Where the trail sits relative to the wells: fraction of trail mass by band of the nearest body's support radius.
|
||||
var bands = new double[5]; double tot = 0;
|
||||
for (var y = 0; y < G; y++) for (var x = 0; x < G; x++)
|
||||
{
|
||||
var p = (new float2(x + .5f, y + .5f) / G - .5f) * 2 * R; var best = 9f;
|
||||
foreach (var b in zone.PlanetInstances.Values) { var u = length(p - b.Orbit.Position) / (b.GravityWellRadius / 2); best = Math.Min(best, u); }
|
||||
var band = best < .1f ? 0 : best < .3f ? 1 : best < .6f ? 2 : best < 1f ? 3 : 4;
|
||||
bands[band] += a1[y * G + x]; tot += a1[y * G + x];
|
||||
}
|
||||
_o.AppendLine(string.Format(inv, "trail mass by u=r/rs of nearest well: core<.1 {0:P1} | .1-.3 {1:P1} | .3-.6 {2:P1} | .6-1 {3:P1} | outside {4:P1}", bands[0] / tot, bands[1] / tot, bands[2] / tot, bands[3] / tot, bands[4] / tot));
|
||||
Ascii("slope", slope, G, false);
|
||||
Ascii("suitability (closed form, Deposition(slope))", suit, G, false);
|
||||
Ascii("physarum A (her rules, log)", a1, G, true);
|
||||
Ascii("physarum B (her rules + global flow, log)", b1, G, true);
|
||||
File.WriteAllText("/tmp/probe/fauna-placement.txt", _o.ToString());
|
||||
}
|
||||
}
|
||||
141
docs/research/2026-10-13-fauna-placement-probe/output.txt
Normal file
141
docs/research/2026-10-13-fauna-placement-probe/output.txt
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
==== PROBE OUTPUT
|
||||
slope-grid-ms 217
|
||||
globalflow horizontal mean 6.063 max 12.247 |y| mean 2.141 u/s; rail speed 6.283
|
||||
physarum G=256 agents=40000 steps=600 ms A1 9031 A2 8600 B(flow) 9421
|
||||
hash A1 fb0ffa1ec11000d3 A2 fb0ffa1ec11000d3 equal True
|
||||
corr(A, closed-form suitability) 0.275; corr(B, suitability) 0.247; corr(A, B) 0.339
|
||||
corr(A, blurred suitability) 0.287
|
||||
trail mass by u=r/rs of nearest well: core<.1 5.7 % | .1-.3 35.6 % | .3-.6 54.1 % | .6-1 4.7 % | outside 0.0 %
|
||||
@@MAP slope
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| ................ |
|
||||
| ........................ |
|
||||
| .............................. |
|
||||
| ................................... |
|
||||
| ...:::::::::.::::::::::::::::........... |
|
||||
| ..:-==+++==-:::::----------::::::......... |
|
||||
| ..:-+****+++=------===========----:::........ |
|
||||
| .:-=+**+-:-=-::-====++++++++++***+==-::...... |
|
||||
| ...:=+**+=:::..:-=+++++++++++*##%%%#*+-::...... |
|
||||
| ...::-=++==-:.:-=+++=========+++*#%%%*=-:....... |
|
||||
| .....::-------==+++==-----::....-*%%%*+-::...... |
|
||||
| .......:::--===+++==-:.......:-=*#%##+=-:....... |
|
||||
| .......:::--===+++=--:....:--==+****+=-::....... |
|
||||
| .......:::--==+*##*-..::---==++++++=--:::....... |
|
||||
| ........:::--=+#@@#=+=======++++===--:::........ |
|
||||
| ........:::--=+#@@%#*++++++++====--:::........ |
|
||||
| ........::::--==++============---:::........ |
|
||||
| .........::::::::::----===-----::::......... |
|
||||
| .......::::::... ..:::---::::::......... |
|
||||
| ....:::------:::...:::::::::........... |
|
||||
| ...::--=====--------::::............. |
|
||||
| ...:-==++==-:::--===--::........... |
|
||||
| .::-==+++=-. .-=====-::........ |
|
||||
| ..:-==+++==-:..-+++=-::.... |
|
||||
| .::-==+++++=-.:+##*=-:.. |
|
||||
| ..:--==++***+=*#%*=:.. |
|
||||
| ..:--=+*#%%%%#+-.. |
|
||||
| ..:-=++=-:. |
|
||||
@@MAP suitability (closed form, Deposition(slope))
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| .. |
|
||||
| .......................... |
|
||||
| .:--===-::......:::::::::...... |
|
||||
| .:=+++++==-:.::::---------::::::... |
|
||||
| ..-+*+=:.:::..:-----=========+++=--:.. |
|
||||
| .-=++=:... .:-==========++*#####+=:.. |
|
||||
| ..:-==--.. .:-===---------===+*%%#+-:. |
|
||||
| ..::::.::--===--::...... .:+#%#*=:.. |
|
||||
| ....:::--===--:. .:-+*##*=-:. |
|
||||
| ...::---===-:.. .::--=++++=-:.. |
|
||||
| ....::-=+*#+: ...::--======-::... |
|
||||
| ...::-=*@@*-=-------====---::... |
|
||||
| ...::-=#@@%#+========---:::... |
|
||||
| ....::--==-----------:::.... |
|
||||
| ..........::::---::::..... |
|
||||
| ...... ....:....... |
|
||||
| ...::::::... .......... |
|
||||
| ..::----::::::::..... |
|
||||
| .::-==--:...::--::... |
|
||||
| ..:--===-. .-----:.. |
|
||||
| .:--===--:. .:===-:.. |
|
||||
| ..:--=====-: .=**+-.. |
|
||||
| .::--==+**=-+##*-. |
|
||||
| ..::-=+#####*=: |
|
||||
| .:-===-:. |
|
||||
@@MAP physarum A (her rules, log)
|
||||
| . |
|
||||
| -. |
|
||||
| .:=.. .. :-:. |
|
||||
| .:. .. .:-+ ..-:. ::-. :. |
|
||||
| .:==: ... .:=*=--*- .: .-: |
|
||||
| ..:-=:-=-:.. .:-+++#:. .:. .::. .:: |
|
||||
| . .. .:=#*=-:. .::=+#*: .. ..+:::-==. |
|
||||
| .:.....*#*:::....-%*=: . ...:-##::-:. |
|
||||
| ..::::..:==::...-+%=:.. : #@=-:-:::-=+**=::.. .... |
|
||||
| ..:-==*+==*=. ::-%#=:.::-*%*+===+++*++-:. ........ . |
|
||||
| ..:-=+====-:::=*@+:::-=+#++-=++-==:..:... ...... ::. |
|
||||
| ... ::--=-+==+**##%*-:-===+:..::..:-:::..:...::=**+=: |
|
||||
| ...:-.:====-:+#*++=.--+*=:.:--=-=+=-=:-==+*++=:.. |
|
||||
| .::.::.:==-::::=----::+*#*+=====++*-..=*=-:.... |
|
||||
| ..:-=++*###%#*+=:.. .-=++-::::-.:---==**+****+=:::.. ... |
|
||||
| ..:-=======-:=+=****#++=-::----=+++=-:.. .......:...... |
|
||||
| ......::::---=--==+-=+*#*+-:-===+=-===-::::::::.... ... |
|
||||
| .... .. ..:-+*##-::.::=*=+===##*+-----=+++*#######++====-:. |
|
||||
| .::-==+*#*=-...===--=-::++::-::-----===--:::::..... |
|
||||
| .=+****+=-:::=-::==+-+++ :::-++++-==+*=:........ |
|
||||
| .:-==-:::.........:+*+=+*#+=-=*++*=++*%%*+=: . . .:::: |
|
||||
| ..::::.. .-=***+---=+=+%##*=-::+###%#-...... ..:. |
|
||||
| ....:--:--+**###+-=-:..--+##*--. ...:-=#*++*++-:... |
|
||||
| ..::=+###+=-====--.:::=+*#+=-:...::-+*+:. :+++=:. |
|
||||
| -=+=:....:-.::::--=-=+*#*+-:::.:---=-=: .. |
|
||||
| .:-.....::-==-=-==-==--=:+*+++=====+=...... |
|
||||
| .:::+*%###*=-=-::=+===--=-::-=+*:.. . |
|
||||
| :.-++...=++*=...::=**#-:::.. ..:==. |
|
||||
| .:=-:-. .:==*-.:- --=##= .:.. .-. |
|
||||
| .::::-:-=-::===+:...:+#. .. |
|
||||
| ..:=*--::---=--:...-- |
|
||||
| ..:.:=-==-::. . |
|
||||
@@MAP physarum B (her rules + global flow, log)
|
||||
| . |
|
||||
| .-.. . -:. |
|
||||
| ..=:. . .. :=+. |
|
||||
| :::-:... .. +*:.. . |
|
||||
| . .. :..::...:....===... . .. .. |
|
||||
| . .::.. :.. .. .+- ... . . ..::. |
|
||||
| .-=-...:+#+--: .:=+%%-:-:...:::.%%=::... |
|
||||
| .:: .:.. .:+#*=-:.. .=+#@+::...:.::+++-... |
|
||||
| ....:.....:.:+*+. ..:::%%:: ..--+#=.:.. ... |
|
||||
| ::: ...--:...+%%-.::.:+%+::.::-=*#-:.. .... . |
|
||||
| ..=%#+-::...-*%#-::.:-=+*::..:#*=::.... ..:.:=---=- |
|
||||
| .+=+:--::=+%@%*=--*+***-:.-%==:...-=-##+*==**=-: |
|
||||
| . .. ..=-::-==::-++=:--=--==-+++:. .-=++*+:::::.... |
|
||||
| .. ..:. .::-:.:-.:..:-::-=--:-**#++=--::--=--::: ... |
|
||||
| .:-=----=+*+=+=-:.:--:=%#*=+=----+-=-=-+*+ .:::... .. |
|
||||
| ...::.:-*+==:-:-*%*#**#**=:....:.-=++-:. .-=-::.. . . |
|
||||
| .. .-==--=====+::::-***=:-=--+*=::-==+=+#@#--==-..::. |
|
||||
| :+-:-=+=-.: ---++*++##**-.. :--==*+---=*+==+==-:. |
|
||||
| ... . ---=+*+--:--+*+*+=--:*#--#*-.::.. .. . ...:..... |
|
||||
| ...:-=+*+=--::===+**=:+#-:.=#*::--**%@#*-..... ........ |
|
||||
| :==+=+=:::==..:.:..==+=-===--#*--.:.:=+#*##::. . ...: |
|
||||
| ..:::....::::=*+-::--==+%@+:: ...:-*%*+#+..:::. |
|
||||
| ....::.:--+*+**=:..:-=+%@#+=: .::=-:-:=**++=-. |
|
||||
| :-+*+*+**#*+-:::-=-=+#*==*=:.:--:.....:=+-:. |
|
||||
| .=*+==+-::--::-:--*+=--::.-#%+::::.:. .:=-. |
|
||||
| .:**=+-=-..:-:.-*+***=..:...:-++*-.... .:: |
|
||||
| .------:--=--:-**#*--=-..:... -%- . |
|
||||
| .::-:::-**+=+*=:++--#%*-.: .::--. |
|
||||
| ........=*=..-..-=:..=*#-..-. .::- |
|
||||
| ..-=**#- .:-*+: .::+= . . |
|
||||
| :-*:...:=.=: ::::: |
|
||||
| .:++.#-::.:: |
|
||||
ygg-verify: exit 0
|
||||
Loading…
Reference in a new issue