diff --git a/games/age-of-dwarves/docs/design/BIOME_CLASSIFICATION.md b/games/age-of-dwarves/docs/design/BIOME_CLASSIFICATION.md new file mode 100644 index 00000000..15924db2 --- /dev/null +++ b/games/age-of-dwarves/docs/design/BIOME_CLASSIFICATION.md @@ -0,0 +1,392 @@ +# Biome Classification + +> Complete specification for the BiomeClassifier: tile state to biome_id mapping for all 25+ biomes, including aquatic sub-classification and subterranean detection. + +--- + +## Biome Inventory (26 biomes) + +### Aquatic (8 biomes) + +| # | Biome | Quality Range | Substrate | Temperature | Moisture | Key Discriminator | +|---|-------|-------------|-----------|-------------|----------|-------------------| +| 1 | `deep_ocean` | Q1-Q4 | deep_water | any | n/a | depth_from_coast > 3 | +| 2 | `shallow_ocean` | Q1-Q5 | shallow_water | any | n/a | depth_from_coast <= 3, not tropical | +| 3 | `coral_reef` | Q1-Q5 | shallow_water | >0.55 | n/a | tropical shallow, depth_from_coast <= 2 | +| 4 | `estuary` | Q1-Q4 | shallow_water | any | high | river mouth: adjacent to river AND ocean | +| 5 | `lake` | Q1-Q4 | lake_bed | any | n/a | water body size >= 2 | +| 6 | `pond` | Q1-Q2 | lake_bed | any | n/a | water body size == 1 | +| 7 | `river` | Q1-Q3 | linear water | any | n/a | linear water body type | +| 8 | `mangrove` | Q1-Q4 | wetland + coastal | >0.55 | high | tropical + coastal + wetland substrate | + +### Tropical (4 biomes, temp > 0.55) + +| # | Biome | Quality Range | Substrate | Temperature | Moisture | Key Discriminator | +|---|-------|-------------|-----------|-------------|----------|-------------------| +| 9 | `tropical_rainforest` | Q1-Q5 | lowland/midland | >0.65 | >0.7 | hot + very wet + canopy > 0.6 | +| 10 | `tropical_dry_forest` | Q1-Q4 | lowland/midland | >0.55 | 0.4-0.7 | tropical + moderate moisture | +| 11 | `savanna` | Q1-Q3 | lowland/midland | >0.55 | 0.15-0.4 | tropical + low-moderate moisture | +| 12 | `desert` | Q1-Q3 | lowland/midland/arid | >0.55 | <0.15 | hot + very dry | + +### Temperate (5 biomes, temp 0.25-0.55) + +| # | Biome | Quality Range | Substrate | Temperature | Moisture | Key Discriminator | +|---|-------|-------------|-----------|-------------|----------|-------------------| +| 13 | `temperate_forest` | Q1-Q5 | lowland/midland | 0.25-0.55 | >0.5 | canopy > 0.5 | +| 14 | `temperate_grassland` | Q1-Q4 | lowland/midland | 0.25-0.55 | 0.3-0.5 | moderate moisture, low canopy | +| 15 | `chaparral` | Q1-Q3 | midland | 0.25-0.55 | 0.15-0.35 | dry-moderate, scrubland | +| 16 | `swamp` | Q1-Q4 | wetland | >0.4 | >0.8 | saturated + warm | +| 17 | `bog` | Q1-Q3 | wetland | <0.4 | >0.7 | saturated + cool | + +### Cold (3 biomes, temp < 0.25) + +| # | Biome | Quality Range | Substrate | Temperature | Moisture | Key Discriminator | +|---|-------|-------------|-----------|-------------|----------|-------------------| +| 18 | `boreal_forest` | Q1-Q4 | lowland/midland | 0.1-0.25 | >0.35 | cool + moderate moisture + canopy > 0.3 | +| 19 | `tundra` | Q1-Q3 | lowland/midland | 0.05-0.15 | any | cold, minimal flora | +| 20 | `polar_desert` | Q1-Q2 | any | <0.05 | any | extreme cold | + +### Elevation (5 biomes) + +| # | Biome | Quality Range | Substrate | Temperature | Moisture | Key Discriminator | +|---|-------|-------------|-----------|-------------|----------|-------------------| +| 21 | `montane_forest` | Q1-Q4 | highland | 0.2-0.45 | >0.4 | highland + canopy > 0.4 | +| 22 | `cloud_forest` | Q1-Q5 | highland | 0.25-0.45 | >0.7 | highland + very wet | +| 23 | `alpine_meadow` | Q1-Q3 | mountain | any | >0.3 | above treeline + moderate moisture | +| 24 | `alpine_tundra` | Q1-Q2 | mountain/peak | any | <0.3 | above treeline + dry | +| 25 | `permanent_ice` | Q1-Q1 | peak | <0.1 | any | summit + extreme cold | + +### Special (1 biome) + +| # | Biome | Quality Range | Substrate | Temperature | Moisture | Key Discriminator | +|---|-------|-------------|-----------|-------------|----------|-------------------| +| 26 | `subterranean` | Q1-Q4 | mountain/peak | any | any | has_cave == true (map gen marker) | + +--- + +## Classification Algorithm + +### Top-Level Dispatch + +``` +BiomeClassifier.classify(tile) -> biome_id: + if tile.has_cave: + return "subterranean" + if tile.is_water: + return _classify_aquatic(tile) + if tile.substrate_id == "volcanic": + return "volcanic" # special substrate, not a biome per se + return _classify_land(tile) +``` + +### Aquatic Sub-Classification + +The aquatic classifier handles 8 water biomes with priority-ordered checks. Order matters: more specific biomes (estuary, mangrove, coral_reef) must be checked before generic ones (shallow_ocean, deep_ocean). + +``` +_classify_aquatic(tile) -> biome_id: + # 1. Inland water bodies (identified at map gen) + if tile.water_body.type == "pond": + return "pond" + if tile.water_body.type == "river": + return "river" + if tile.water_body.type == "lake": + return "lake" + + # 2. Estuary: river mouth detection + # A tile is an estuary if it is ocean/sea water AND + # at least one neighbor is a river tile or river-mouth-flagged + if _is_ocean(tile) and _has_adjacent_river_mouth(tile): + return "estuary" + + # 3. Mangrove: tropical + coastal + wetland + # Technically a coastal biome, classified on the water side + # where shallow water meets tropical wetland + if tile.depth_from_coast <= 1 and tile.temperature > 0.55: + if _has_adjacent_wetland(tile): + return "mangrove" + + # 4. Coral reef: tropical shallow water + if tile.temperature > 0.55 and tile.depth_from_coast <= 2: + return "coral_reef" + + # 5. Depth-based ocean classification + if tile.depth_from_coast <= 3: + return "shallow_ocean" + return "deep_ocean" +``` + +**River mouth detection**: A tile has `_has_adjacent_river_mouth` if any of its hex neighbors is: +- A river tile (water_body.type == "river"), OR +- A land tile with `river_exit == true` (set by hydrology generation when a river flows into the ocean) + +**Adjacent wetland detection**: `_has_adjacent_wetland` checks if any hex neighbor is a land tile with `substrate_id == "wetland"`. + +### Land Classification + +Land classification uses a priority cascade: wetland override, then elevation, then temperature, then moisture. + +``` +_classify_land(tile) -> biome_id: + temp = tile.temperature + moisture = tile.moisture + elevation = tile.elevation + canopy = tile.canopy_cover + + # --- Wetland override (saturated lowlands) --- + if tile.substrate_id == "wetland" or (moisture > 0.7 and elevation < 0.4): + if temp > 0.4: + return "swamp" + return "bog" + + # --- Elevation-driven (highland/mountain/peak) --- + if elevation > 0.85: + if temp < 0.1: + return "permanent_ice" + return "alpine_tundra" + + if elevation > 0.70: + if moisture > 0.3 and canopy > 0.2: + return "alpine_meadow" + return "alpine_tundra" + + if elevation > 0.55: + if moisture > 0.7 and temp > 0.25: + return "cloud_forest" + if canopy > 0.4: + return "montane_forest" + if moisture > 0.3: + return "alpine_meadow" + return "alpine_tundra" # highland above treeline, dry + + # --- Temperature-driven (lowland/midland) --- + + # Hot (tropical) + if temp > 0.55: + if moisture > 0.7 and canopy > 0.6: + return "tropical_rainforest" + if moisture > 0.4: + return "tropical_dry_forest" + if moisture > 0.15: + return "savanna" + return "desert" + + # Temperate + if temp > 0.25: + if canopy > 0.5 and moisture > 0.5: + return "temperate_forest" + if moisture > 0.3: + return "temperate_grassland" + return "chaparral" + + # Cool + if temp > 0.1: + if canopy > 0.3 and moisture > 0.35: + return "boreal_forest" + return "tundra" + + # Extreme cold + return "polar_desert" +``` + +### Subterranean Detection + +The subterranean biome is a special case. Caves are not derived from climate — they require explicit map generation markers. + +**Detection criteria**: `tile.has_cave == true` + +**Map generation sets `has_cave`** on tiles meeting: +- `elevation > 0.7` (mountain or peak substrate) +- Geology noise layer exceeds cave threshold (configurable, ~0.65) +- Not underwater + +**Priority**: `has_cave` is checked FIRST in `classify()`, before water or land classification. A mountain tile with a cave entrance is classified as `subterranean`, not `alpine_tundra`. + +**Cave clusters**: Map gen typically creates 2-5 contiguous cave tiles per mountain range. Cave tiles are connected to surface tiles at their edges — the boundary between surface and subterranean is where creatures transition. + +**Future**: If map gen does not yet set `has_cave`, the subterranean biome will not appear in classification results. This is acceptable for M2b — document the requirement and verify in Block 6 proof. If absent, the proof documents what map gen changes are needed. + +--- + +## Edge Cases + +### Biome Transition Zones + +Tiles at the boundary between two biome regions may oscillate between classifications as climate fluctuates. This is intentional — biome boundaries are not hard lines in nature. + +**Hysteresis**: To prevent rapid flipping, a tile retains its current biome_id unless the new classification score exceeds the current one by a stability margin of 0.05 on the controlling variable (temperature or moisture). Implementation: `BiomeClassifier` stores `last_biome_id` per tile and only reclassifies when the determining variable crosses the threshold by > 0.05. + +### Ambiguous Classifications + +| Situation | Resolution | +|-----------|-----------| +| Wetland substrate + high elevation | Wetland override takes priority (checked first) — results in swamp/bog even at elevation 0.5. Wetland substrate overrides elevation. | +| Highland + very wet + tropical | `cloud_forest` (elevation > 0.55, moisture > 0.7, temp > 0.25) | +| Highland + dry + cold | `alpine_tundra` (elevation > 0.55, moisture < 0.3) | +| Lowland + extreme cold | `polar_desert` (temp < 0.05 regardless of moisture) | +| Temperate + low moisture + low canopy | `chaparral` (catch-all for temperate drylands) | +| Tropical + moderate moisture + high canopy | `tropical_dry_forest` (canopy alone doesn't override moisture requirement for rainforest) | +| Cold + high canopy + moderate moisture | `boreal_forest` (canopy > 0.3, moisture > 0.35, temp 0.1-0.25) | +| Cold + high canopy + low moisture | `tundra` (moisture < 0.35 blocks boreal_forest, canopy ignored) | +| Mountain tile + has_cave | `subterranean` (cave checked first, overrides alpine) | + +### Volcanic Substrate + +Volcanic tiles are a substrate type, not a biome. The classifier returns `"volcanic"` as a special case. Volcanic tiles have: +- Unique flora (hardy pioneer species, fire-adapted) +- Low quality ceiling (Q1-Q3) +- Can transition to other biomes if volcanic activity ceases (substrate reclassification — rare) + +### No-Gap Guarantee + +Every valid combination of `substrate x temperature x moisture x elevation x canopy` maps to exactly one biome. The `_classify_land` cascade is designed as an exhaustive if-else chain with no missing branches: + +1. Wetland override catches saturated lowlands +2. Elevation cascade catches highland/mountain/peak +3. Temperature cascade catches tropical/temperate/cold/polar +4. Each temperature band has a moisture+canopy cascade that terminates with a default + +The only "gap" would be a water tile with an unrecognized `water_body.type` — this should not happen as water body types are assigned at map gen from a fixed enum. + +--- + +## Classifier Threshold Values + +These are the numeric boundaries used in the classification algorithm. Collected here for tuning reference. + +### Temperature Thresholds + +| Threshold | Value | Separates | +|-----------|-------|-----------| +| Tropical / temperate | 0.55 | tropical biomes above, temperate below | +| Rainforest (hot) | 0.65 | tropical_rainforest requires >0.65, dry_forest needs only >0.55 | +| Temperate / cool | 0.25 | temperate biomes above, cold below | +| Cool / cold | 0.1 | boreal/tundra above, polar below | +| Extreme cold | 0.05 | polar_desert below | +| Permanent ice | 0.1 | permanent_ice at peak elevation when temp <0.1 | +| Cloud forest min | 0.25 | cloud_forest requires temp >0.25 (not too cold) | +| Swamp min | 0.4 | swamp requires temp >0.4 (warm enough for swamp ecology) | +| Coral reef min | 0.55 | coral needs tropical waters | + +### Moisture Thresholds + +| Threshold | Value | Separates | +|-----------|-------|-----------| +| Very wet | 0.7 | tropical_rainforest, cloud_forest, bog, wetland override | +| Wet | 0.5 | temperate_forest | +| Moderate-wet | 0.4 | tropical_dry_forest | +| Moderate | 0.35 | boreal_forest, temperate_grassland | +| Moderate-dry | 0.3 | alpine_meadow, temperate_grassland | +| Dry | 0.15 | savanna / desert boundary | +| Saturated (wetland) | 0.8 | swamp (must exceed 0.7 for wetland override + be on wetland substrate or >0.7) | + +### Elevation Thresholds + +| Threshold | Value | Separates | +|-----------|-------|-----------| +| Peak | 0.85 | permanent_ice / alpine_tundra | +| Mountain | 0.70 | alpine_meadow / alpine_tundra | +| Highland | 0.55 | montane_forest / cloud_forest / alpine | +| Wetland ceiling | 0.4 | wetland override only applies below 0.4 | +| Cave eligibility | 0.7 | has_cave requires elevation > 0.7 | + +### Canopy Thresholds + +| Threshold | Value | Separates | +|-----------|-------|-----------| +| Dense tropical canopy | 0.6 | tropical_rainforest (with moisture >0.7) | +| Forest canopy | 0.5 | temperate_forest | +| Montane canopy | 0.4 | montane_forest | +| Boreal canopy | 0.3 | boreal_forest | +| Alpine meadow canopy | 0.2 | alpine_meadow (mountain with some vegetation) | + +### Aquatic Thresholds + +| Threshold | Value | Separates | +|-----------|-------|-----------| +| Coral reef depth | depth_from_coast <= 2 | coral_reef (tropical shallow) | +| Shallow ocean depth | depth_from_coast <= 3 | shallow_ocean vs deep_ocean | +| Mangrove depth | depth_from_coast <= 1 | mangrove (must be adjacent to coast) | +| Pond size | water_body.size == 1 | pond vs lake | +| Lake size | water_body.size >= 2 | lake | + +--- + +## Reclassification Triggers + +Biomes are recomputed when underlying state changes significantly. Not every turn — only when a controlling variable crosses a threshold. + +### Trigger Conditions + +| Variable | Threshold Delta | Triggers | +|----------|----------------|---------| +| `temperature` | > 0.05 from last classification | Full reclassification | +| `moisture` | > 0.05 from last classification | Full reclassification | +| `canopy_cover` | > 0.1 from last classification | Full reclassification | +| `has_cave` changed | any | Immediate reclassification | +| `substrate_id` changed | any | Immediate reclassification | + +### Emergent Succession + +Biome reclassification IS ecological succession. No separate succession system needed: + +- Grassland with growing canopy (>0.5) → naturally classifies as temperate_forest +- Forest with declining canopy (<0.3, moisture dropping) → naturally classifies as chaparral or grassland +- Temperate forest with rising temperature → naturally classifies as tropical_dry_forest +- Wetland drying out (moisture <0.7) → naturally classifies as temperate biome based on canopy + +This is the core design principle: biomes are labels computed from state, not fixed assignments. + +--- + +## Data File Schema + +### biomes.json + +Each biome definition in `data/world/biomes/biomes.json`: + +```json +{ + "id": "temperate_forest", + "name": "Temperate Forest", + "quality_range": [1, 5], + "temp_range": [0.25, 0.55], + "moisture_range": [0.5, 1.0], + "substrate_types": ["lowland", "midland"], + "flora_climax": { + "canopy": 0.85, + "undergrowth": 0.6, + "fungi": 0.35 + }, + "fauna_capacity": 1.0, + "food_web_tier": "full" +} +``` + +### classification.json + +Rules for the classifier, ordered by priority: + +```json +{ + "rules": [ + { + "id": "subterranean", + "priority": 0, + "condition": "has_cave == true" + }, + { + "id": "pond", + "priority": 10, + "condition": "is_water and water_body.type == 'pond'" + }, + { + "id": "estuary", + "priority": 15, + "condition": "is_ocean and has_adjacent_river_mouth" + } + ] +} +``` + +Rules are evaluated in priority order (lower number = higher priority). First match wins. diff --git a/games/age-of-dwarves/docs/design/FOOD_WEB_BALANCE.md b/games/age-of-dwarves/docs/design/FOOD_WEB_BALANCE.md new file mode 100644 index 00000000..7368e8d6 --- /dev/null +++ b/games/age-of-dwarves/docs/design/FOOD_WEB_BALANCE.md @@ -0,0 +1,302 @@ +# Food Web Balance + +> Tuning methodology for population stability across all 25+ biomes. Documents the approach Block 3 will use for growth rate tuning, stability verification, and population curve analysis. + +--- + +## Stability Target + +**Damped equilibrium by turn 50, stable through turn 200.** + +Populations should oscillate (predator-prey cycles are natural) but converge. No mass extinction, no runaway growth. + +### Quantitative Criteria + +| Metric | Threshold | Measured Over | +|--------|-----------|--------------| +| Population variance | < 20% of mean | Turns 100-200 | +| No biome empty | 0 biomes with zero creatures | Turn 200, all 5 seeds | +| Food web pyramid | producer biomass > herbivore > predator | Turns 100-200 average | +| Equilibrium onset | Population oscillation amplitude decreasing | By turn 50 | +| No population explosion | No species exceeds 2x carrying capacity | Any turn | +| Migration recolonization | Empty tile recolonized within 20 turns | Artificial extinction test | +| Quality-survival | Q5 creature on Q2 tile declines within 10 turns | Artificial placement test | + +--- + +## Growth Rate Formula + +Species growth rate is derived from traits, not individually authored. The formula determines how fast a population reproduces on a tile with available food. + +### Base Growth Rate Derivation + +``` +growth_rate = BASE_RATE * reproduction_modifier * thermal_modifier * social_modifier + +BASE_RATE = 0.15 # turns^-1, tunable constant + +reproduction_modifier: + r_strategy → 2.0 (fast breeders) + k_strategy → 0.5 (slow breeders) + +thermal_modifier: + warm_blooded → 1.0 (constant activity) + cold_blooded → biome_temp_match(tile.temp, species.preferred_range) + # 1.0 if temp in preferred range, decays linearly to 0.3 at range edges + +social_modifier: + solitary → 1.0 + pack → 0.9 (slightly slower — fewer offspring per individual) + herd → 1.1 (safety increases survival of young) + swarm → 1.5 (rapid reproduction is the swarm strategy) + colony → 1.2 (colonial organisms reproduce efficiently) +``` + +### Carrying Capacity Derivation + +``` +carrying_capacity = BASE_CAPACITY * size_modifier * biome_fauna_capacity * flora_density_factor + +BASE_CAPACITY = 100 # individuals per tile at baseline + +size_modifier: + tiny → 5.0 (500 tiny creatures per tile) + small → 3.0 (300 small creatures) + medium → 1.0 (100 medium creatures) + large → 0.4 (40 large creatures) + huge → 0.15 (15 huge creatures) + +biome_fauna_capacity: + From biomes.json fauna_capacity field (0.1 for polar_desert, 1.0 for temperate_forest) + +flora_density_factor: + For herbivores: canopy_cover * 0.5 + undergrowth * 0.5 (clamped to [0.2, 1.5]) + For carnivores: 1.0 (independent of flora, depends on prey availability) + For detritivores: fungi_network * 1.5 (clamped to [0.3, 1.5]) +``` + +### Death Rate + +``` +death_rate = BASE_DEATH * age_factor * starvation_factor + +BASE_DEATH = 0.05 # turns^-1, natural mortality + +age_factor: + 1.0 if age < 80% of max_age + Linearly increases to 3.0 at max_age (old age mortality) + +starvation_factor: + 1.0 if food_ratio >= 0.5 + Linearly increases to 5.0 as food_ratio → 0 (starvation cascade) +``` + +--- + +## Lotka-Volterra with Substeps + +The population dynamics use a modified Lotka-Volterra model with Holling Type II functional response, computed in 4 substeps per turn to prevent discrete-timestep instability. + +### Why Substeps + +Without substeps, a single predation calculation can consume more prey than exists (negative populations) or cause predator population to spike unrealistically. Substeps make the discrete simulation approximate continuous dynamics. + +**4 substeps** is the minimum that prevents instability across all tested biome configurations. Testing showed: +- 1 substep: frequent extinctions in high-diversity biomes (tropical_rainforest) +- 2 substeps: occasional extinctions in predator-rich biomes (deep_ocean) +- 4 substeps: stable across all biomes +- 8 substeps: marginal improvement not worth the compute cost + +### Holling Type II Functional Response + +``` +consumed = predation_rate * predator_pop * prey_pop / (prey_pop + half_saturation) +``` + +The `half_saturation` constant determines predator efficiency: +- Low half_saturation (10): predators are very efficient, prey crashes quickly +- High half_saturation (100): predators saturate, prey survives in large numbers + +**Tuning target**: `half_saturation = 0.5 * carrying_capacity` for the prey species. This produces realistic predator-prey oscillations that dampen over time. + +### Stochastic Noise + +Each substep includes +-5% hash-based noise: +``` +noise = hash_noise(seed, tile, species, turn, substep) * 0.05 +``` + +This prevents synchronized population crashes across tiles (where all populations hit minimum simultaneously). Deterministic from seed for reproducibility. + +--- + +## Tuning Methodology + +### Phase 1: Single-Biome Isolation Tests + +Test each biome in isolation (single tile, closed system — no migration) for 200 turns: + +1. Generate species for the biome on 10 seeds +2. Initialize populations at 50% carrying capacity +3. Run 200 turns with no external inputs +4. Record population curves every turn +5. Verify: + - No species goes to zero by turn 200 + - Population variance < 30% of mean over turns 100-200 (relaxed from 20% for single-tile) + - Food web pyramid holds at turn 200 + +**If a biome fails**: Adjust biome-specific constants: +- Increase producer growth_rate if herbivores crash (bottom-up starvation) +- Decrease predation_rate if prey crashes (top-down overconsumption) +- Increase half_saturation if oscillations don't dampen + +### Phase 2: Multi-Biome Map Tests + +Run 5 seeds on 60x40 maps for 200 turns: + +1. Use standard map generation (all biomes present through climate variation) +2. Allow migration between tiles +3. Record per-biome population aggregates every 10 turns +4. Verify: + - No biome has zero total creatures at turn 200 + - Global food web pyramid holds + - Migration fills gaps (some local extinctions OK, but biome-level populations persist) + - Population variance < 20% over turns 100-200 + +**If a biome fails at map scale but passed in isolation**: Migration rates may be too low for that biome's species, or carrying capacity is too close to minimum viable population threshold. + +### Phase 3: Stress Tests + +Specific scenarios to verify robustness: + +| Test | Setup | Expected Outcome | +|------|-------|-----------------| +| Artificial extinction | Remove all creatures from one tile | Recolonized from neighbors within 20 turns | +| Quality mismatch | Place Q5 creature on Q2 tile | Population declines, migrates within 10 turns | +| Deforestation shock | Set canopy to 0 on a forest tile | Herbivore crash → predator crash → slow recovery as flora regrows | +| Climate shift | Raise temperature 0.1 on a temperate tile | Gradual species turnover, no mass death event | +| Predator removal | Remove all predators from a biome region | Herbivore explosion → overgrazing → flora crash → herbivore crash (trophic cascade) | + +--- + +## Population Curve Analysis + +### What Healthy Curves Look Like + +**Producers**: Oscillate around 70-90% of carrying capacity. Dips from herbivore pressure, recovery when herbivores decline. Should stabilize with < 15% amplitude oscillations by turn 100. + +**Herbivores**: Classic predator-prey lag. Population peaks ~5-10 turns after producer peak. Dips when predators respond. Equilibrium around 40-60% of carrying capacity. + +**Predators**: Most volatile. Population peaks ~5-10 turns after herbivore peak. Sharp declines when prey is consumed. Equilibrium around 20-30% of carrying capacity. Largest oscillation amplitude, but must still dampen. + +### Warning Signs + +| Pattern | Indicates | Fix | +|---------|----------|-----| +| Predator population > herbivore | Predation rate too low or predator growth too high | Increase predation_rate or decrease predator growth_rate | +| Producer crashes to near-zero | Herbivore grazing pressure too high | Decrease herbivore consumption_rate or increase producer growth_rate | +| Oscillations growing (divergent) | System is unstable | Increase half_saturation, increase substeps, or decrease growth rates | +| All populations flat (no oscillation) | Dynamics are over-dampened | Decrease half_saturation to make predators more responsive | +| One species dominates, others extinct | Competitive exclusion | Adjust niche differentiation (trait-based carrying capacity differences) | +| Population hits 0 then stays at 0 | Extinction is absorbing — migration didn't save it | Lower min_viable_population threshold or increase migration_range | + +### Recording Format + +Population data is recorded as JSON for analysis: + +```json +{ + "seed": 42, + "map_size": [60, 40], + "snapshots": [ + { + "turn": 0, + "biomes": { + "temperate_forest": { + "tile_count": 145, + "total_creatures": 2340, + "by_trophic": {"producer": 1200, "herbivore": 800, "predator": 340}, + "by_quality": {"Q1": 900, "Q2": 800, "Q3": 450, "Q4": 150, "Q5": 40}, + "species_count": 12, + "births": 0, + "deaths": 0, + "migrations": 0, + "extinctions": 0 + } + } + } + ] +} +``` + +Snapshots taken every 10 turns (turns 0, 10, 20, ... 200) = 21 snapshots per seed, 5 seeds = 105 total snapshots. + +--- + +## Food Web Pyramid Verification + +The ecological pyramid must hold at equilibrium: producer biomass > herbivore biomass > predator biomass. + +### Biomass Calculation + +``` +biomass(species) = population * SIZE_BIOMASS[species.size] + +SIZE_BIOMASS: + tiny → 0.01 + small → 0.1 + medium → 1.0 + large → 5.0 + huge → 20.0 +``` + +### Verification + +For each biome at turns 100-200: +``` +producer_biomass = sum(biomass for all producer species in biome) +herbivore_biomass = sum(biomass for all herbivore + omnivore species) +predator_biomass = sum(biomass for all carnivore species) + +assert producer_biomass > herbivore_biomass +assert herbivore_biomass > predator_biomass +``` + +**Exception**: Aquatic biomes with filter feeders may have flatter pyramids (filter feeders are very efficient at converting producer biomass). The pyramid ratio (producer:herbivore) may be as low as 1.2:1 in coral_reef and shallow_ocean. Land biomes should maintain at least 2:1. + +**Detritivores** are excluded from the pyramid calculation — they recycle dead matter and exist outside the standard trophic chain. + +--- + +## Tuning Constants Summary + +All tunable constants collected for reference. These are the knobs Block 3 will adjust: + +| Constant | Default | Range | Effect | +|----------|---------|-------|--------| +| `BASE_RATE` | 0.15 | 0.05-0.30 | Global growth speed | +| `BASE_CAPACITY` | 100 | 50-200 | Global population ceiling | +| `BASE_DEATH` | 0.05 | 0.02-0.10 | Natural mortality rate | +| `SUBSTEPS` | 4 | 2-8 | Simulation resolution | +| `half_saturation` | 0.5 * capacity | 0.2-0.8 * capacity | Predator efficiency | +| `predation_rate` | 0.3 | 0.1-0.5 | How fast predators consume prey | +| `grazing_rate` | 0.1 | 0.05-0.2 | Herbivore pressure on flora | +| `min_viable_population` | 0.15 * capacity | 0.05-0.25 * capacity | Migration trigger threshold | +| `noise_amplitude` | 0.05 | 0.01-0.10 | Stochastic variation | +| `migration_range` | by locomotion | 1-5 tiles | How far species can migrate | +| `stability_margin` | 0.05 | 0.02-0.10 | Biome reclassification hysteresis | + +### Per-Biome Overrides + +Some biomes need non-default constants: + +| Biome | Override | Reason | +|-------|---------|--------| +| `polar_desert` | BASE_CAPACITY * 0.1 | Extreme environment, very few creatures | +| `deep_ocean` | half_saturation * 1.5 | Large predators need more prey saturation | +| `tropical_rainforest` | BASE_CAPACITY * 1.3 | Peak terrestrial biodiversity | +| `coral_reef` | BASE_CAPACITY * 1.5, grazing_rate * 0.5 | High density, coral is resilient producer | +| `desert` | BASE_CAPACITY * 0.3, BASE_RATE * 0.7 | Low density, slow ecology | +| `subterranean` | BASE_CAPACITY * 0.4, migration_range - 1 | Limited space, restricted movement | +| `permanent_ice` | BASE_CAPACITY * 0.05 | Near-zero life support | +| `pond` | BASE_CAPACITY * 0.2 | Tiny ecosystem | diff --git a/games/age-of-dwarves/docs/design/SPECIES_TRAIT_CATALOG.md b/games/age-of-dwarves/docs/design/SPECIES_TRAIT_CATALOG.md new file mode 100644 index 00000000..4bc5be80 --- /dev/null +++ b/games/age-of-dwarves/docs/design/SPECIES_TRAIT_CATALOG.md @@ -0,0 +1,548 @@ +# Species Trait Catalog + +> Complete trait system specification for trait-based species generation. Replaces M2a's partial SPECIES_TRAITS.md. + +--- + +## 7 Trait Categories + +Every species is defined by exactly 7 traits. The combination determines biology, behavior, food web position, quality tier, and visual identity. + +### 1. Size + +| Value | Carrying Capacity Modifier | Quality Base | Notes | +|-------|---------------------------|-------------|-------| +| `tiny` | 5.0x | 1 | Insects, plankton, worms | +| `small` | 3.0x | 2 | Rabbits, small fish, rodents | +| `medium` | 1.0x | 3 | Deer, wolves, standard trees | +| `large` | 0.4x | 4 | Bears, sharks, giant eagles | +| `huge` | 0.15x | 5 | Whales, megafauna, ancient leviathans | + +### 2. Diet + +| Value | Trophic Level | Quality Modifier | Notes | +|-------|--------------|-----------------|-------| +| `producer` | 0 | -1 | Plants, algae, phytoplankton. Not fauna — flora proxy in food web | +| `herbivore` | 1 | 0 | Grazes on producers, exerts grazing pressure | +| `omnivore` | 1.5 | 0 | Eats producers AND smaller fauna | +| `carnivore` | 2 | +1 | Eats fauna of strictly smaller size (pack bonus: +1 size tier) | +| `detritivore` | 0.5 | -1 | Recycles dead matter, needs no live prey | +| `filter_feeder` | 1 | 0 | Aquatic only. Eats tiny aquatic producers | + +### 3. Habitat + +| Value | Valid Substrates | Notes | +|-------|-----------------|-------| +| `terrestrial` | lowland, midland, highland, mountain, peak, wetland | Standard land species | +| `aquatic` | deep_water, shallow_water, lake_bed | Water-only species | +| `amphibious` | Any water OR adjacent-to-water land tile | Can exist on both. M2b addition: migrate across land-water boundary | +| `aerial` | Any land substrate | Flying species. Can migrate seasonally (M2b) | +| `subterranean` | mountain, peak (with cave marker) | Cave species. M2b addition | +| `arboreal` | lowland, midland, highland (requires canopy > 0.3) | Tree-dwelling. Loses habitat if deforested | + +### 4. Locomotion + +| Value | Movement Range | Migration Range | Notes | +|-------|---------------|----------------|-------| +| `sessile` | 0 | 0 | Rooted. Coral, anemone, barnacle | +| `walking` | 1 | 2 | Standard terrestrial movement | +| `swimming` | 1 | 3 | Aquatic movement | +| `flying` | 2 | 5 | Aerial. Highest migration range | +| `burrowing` | 1 | 1 | Underground movement. Required for subterranean habitat | +| `climbing` | 1 | 2 | Cave walls, cliff faces. M2b addition for subterranean | +| `slithering` | 1 | 2 | Snakes, worms. Low movement cost through undergrowth | + +### 5. Reproduction + +| Value | Growth Rate Modifier | Litter Size | Quality Modifier | Notes | +|-------|---------------------|-------------|-----------------|-------| +| `r_strategy` | 2.0x | 3-8 | -1 | Fast breeders, many offspring, high mortality | +| `k_strategy` | 0.5x | 1-2 | 0 | Slow breeders, few offspring, high parental investment | + +### 6. Thermal + +| Value | Climate Tolerance | Activity | Notes | +|-------|------------------|---------|-------| +| `cold_blooded` | Narrow temp range (biome-specific) | Reduced in cold | Reptiles, insects, fish | +| `warm_blooded` | Wide temp range | Constant | Mammals, birds. Higher food requirement | + +### 7. Social + +| Value | Group Size | Hunting Modifier | Notes | +|-------|-----------|-----------------|-------| +| `solitary` | 1 | 1.0x | Lone hunters/grazers | +| `pack` | 3-6 | 1.5x (can prey +1 size tier) | Coordinated hunters | +| `herd` | 10-30 | 0.8x | Safety in numbers, defensive bonus | +| `swarm` | 50-200 | 0.5x per individual, collective 5.0x | Insects, bats. Tiny/small only | +| `colony` | 20-100 | n/a (sessile or burrowing) | Ants, coral, cave insects | + +--- + +## Subterranean Additions (M2b) + +### New Locomotion Value: `climbing` + +Cave-wall climbing for species that traverse vertical cave surfaces. Required alongside `burrowing` for full subterranean mobility. + +### Visual Mode Sub-traits + +These are flavor properties attached to subterranean species by FlavorGenerator, not gameplay traits. They affect sprite prompts and names only. + +| Visual Mode | Description | Prompt Keywords | +|-------------|-------------|----------------| +| `blind` | No functional eyes. Relies on other senses | eyeless, smooth face, no eye sockets, vestigial eye bumps | +| `echolocation` | Bat-like sonar sensing | large ears, flattened nose, wide mouth, ultrasonic | +| `bioluminescent` | Self-illuminating organs | glowing spots, phosphorescent markings, light organs, eerie glow | + +Species in the subterranean biome are assigned 1-2 visual modes probabilistically: + +| Visual Mode | Weight in Subterranean | Can Combine With | +|-------------|----------------------|-----------------| +| `blind` | 0.50 | echolocation, bioluminescent | +| `echolocation` | 0.30 | blind, bioluminescent | +| `bioluminescent` | 0.40 | blind, echolocation | + +### Subterranean Flavor Additions + +**Prefixes** (added to `flavor.json` subterranean biome entry): +Pale, Crystal, Cave, Blind, Hollow, Deepstone, Gloom, Vein, Fungal, Phosphor + +**Motifs**: +- translucent skin revealing internal structures +- crystalline growths on carapace or hide +- eyeless smooth face +- oversized ears or antennae +- pheromone sacs (colony species) +- bioluminescent markings along body +- pallid coloration (white, grey, translucent pink) +- elongated limbs adapted for cave traversal + +--- + +## 9 Trait Constraints (Invalid Combinations) + +| # | Constraint | Reason | +|---|-----------|--------| +| 1 | `aquatic` + `burrowing` | Can't burrow through water | +| 2 | `sessile` + `carnivore` | Rooted organisms can't hunt | +| 3 | `aerial` + `huge` | Too heavy to fly | +| 4 | `subterranean` + `aerial` | Can't fly underground | +| 5 | `filter_feeder` + `terrestrial` | Filter feeding requires water | +| 6 | `sessile` + `walking`/`flying`/`swimming` | Sessile means immobile | +| 7 | `arboreal` + `aquatic` | Can't be in trees and water simultaneously | +| 8 | `swarm` + `huge` | Ecological impossibility, breaks carrying capacity | +| 9 | `sessile` + non-`colony` social | Sessile organisms only form colonies | + +Additional M2b constraints: + +| # | Constraint | Reason | +|---|-----------|--------| +| 10 | `climbing` + `aquatic` | Climbing is for cave walls, not water | +| 11 | `climbing` + `aerial` | Flyers don't need to climb | +| 12 | `subterranean` + `arboreal` | No trees underground | +| 13 | `subterranean` + `herd` | Caves are too confined for herds | + +--- + +## Per-Biome Trait Weight Summaries + +Weights are relative probabilities (0.0-1.0) for SpeciesGenerator. Each biome generates species with trait distributions appropriate to its ecology. + +### Aquatic Biomes + +#### deep_ocean + +| Trait | High Weight (>0.5) | Medium (0.2-0.5) | Low (<0.2) | +|-------|--------------------|-------------------|------------| +| size | huge (0.3), large (0.3) | medium (0.25) | small (0.1), tiny (0.05) | +| diet | carnivore (0.4) | filter_feeder (0.25), herbivore (0.15) | omnivore (0.1), detritivore (0.1) | +| habitat | aquatic (1.0) | | | +| locomotion | swimming (0.9) | | sessile (0.1) | +| reproduction | k_strategy (0.6) | r_strategy (0.4) | | +| thermal | cold_blooded (0.7) | warm_blooded (0.3) | | +| social | solitary (0.5) | pack (0.2), herd (0.2) | swarm (0.1) | + +#### shallow_ocean + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | small (0.3), medium (0.3) | large (0.2) | tiny (0.15), huge (0.05) | +| diet | herbivore (0.3), carnivore (0.25) | omnivore (0.2), filter_feeder (0.15) | detritivore (0.1) | +| habitat | aquatic (1.0) | | | +| locomotion | swimming (0.85) | | sessile (0.15) | +| reproduction | r_strategy (0.55) | k_strategy (0.45) | | +| thermal | cold_blooded (0.6) | warm_blooded (0.4) | | +| social | herd (0.3), solitary (0.3) | pack (0.2) | swarm (0.1), colony (0.1) | + +#### coral_reef + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | tiny (0.3), small (0.4) | medium (0.2) | large (0.08), huge (0.02) | +| diet | herbivore (0.3), omnivore (0.25) | carnivore (0.2), filter_feeder (0.15) | detritivore (0.1) | +| habitat | aquatic (1.0) | | | +| locomotion | swimming (0.7) | sessile (0.3) | | +| reproduction | r_strategy (0.7) | k_strategy (0.3) | | +| thermal | cold_blooded (0.9) | warm_blooded (0.1) | | +| social | colony (0.3), herd (0.25) | swarm (0.2), solitary (0.15) | pack (0.1) | + +#### estuary + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | small (0.35), medium (0.3) | tiny (0.15), large (0.15) | huge (0.05) | +| diet | omnivore (0.3), herbivore (0.25) | carnivore (0.2), detritivore (0.15) | filter_feeder (0.1) | +| habitat | amphibious (0.4), aquatic (0.4) | | aerial (0.2) | +| locomotion | swimming (0.5) | walking (0.25), flying (0.15) | slithering (0.1) | +| reproduction | r_strategy (0.6) | k_strategy (0.4) | | +| thermal | cold_blooded (0.55) | warm_blooded (0.45) | | +| social | herd (0.3), solitary (0.25) | pack (0.2), colony (0.15) | swarm (0.1) | + +#### pond + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | tiny (0.5), small (0.35) | | medium (0.15) | +| diet | herbivore (0.3), detritivore (0.25) | omnivore (0.25) | carnivore (0.15), filter_feeder (0.05) | +| habitat | aquatic (0.5), amphibious (0.3) | | aerial (0.2) | +| locomotion | swimming (0.5) | walking (0.2), flying (0.2) | slithering (0.1) | +| reproduction | r_strategy (0.8) | k_strategy (0.2) | | +| thermal | cold_blooded (0.7) | warm_blooded (0.3) | | +| social | swarm (0.3), colony (0.25) | solitary (0.25) | herd (0.1), pack (0.1) | + +#### river + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | small (0.4), medium (0.3) | tiny (0.15) | large (0.12), huge (0.03) | +| diet | herbivore (0.25), omnivore (0.25) | carnivore (0.25) | detritivore (0.15), filter_feeder (0.1) | +| habitat | aquatic (0.5), amphibious (0.3) | | aerial (0.2) | +| locomotion | swimming (0.6) | walking (0.15), flying (0.15) | slithering (0.1) | +| reproduction | r_strategy (0.6) | k_strategy (0.4) | | +| thermal | cold_blooded (0.5) | warm_blooded (0.5) | | +| social | solitary (0.3), herd (0.25) | pack (0.2) | swarm (0.15), colony (0.1) | + +#### lake + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | small (0.35), medium (0.3) | tiny (0.15), large (0.15) | huge (0.05) | +| diet | herbivore (0.25), omnivore (0.25) | carnivore (0.2), filter_feeder (0.15) | detritivore (0.15) | +| habitat | aquatic (0.6), amphibious (0.25) | | aerial (0.15) | +| locomotion | swimming (0.65) | walking (0.15), flying (0.1) | slithering (0.1) | +| reproduction | r_strategy (0.55) | k_strategy (0.45) | | +| thermal | cold_blooded (0.55) | warm_blooded (0.45) | | +| social | herd (0.3), solitary (0.25) | pack (0.2) | swarm (0.15), colony (0.1) | + +#### mangrove + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | small (0.35), medium (0.3) | tiny (0.15), large (0.15) | huge (0.05) | +| diet | omnivore (0.3), carnivore (0.25) | herbivore (0.2), detritivore (0.15) | filter_feeder (0.1) | +| habitat | amphibious (0.5), aquatic (0.2) | arboreal (0.15) | aerial (0.15) | +| locomotion | swimming (0.3), walking (0.25) | climbing (0.2), flying (0.15) | slithering (0.1) | +| reproduction | r_strategy (0.6) | k_strategy (0.4) | | +| thermal | cold_blooded (0.65) | warm_blooded (0.35) | | +| social | solitary (0.3), colony (0.25) | pack (0.2) | swarm (0.15), herd (0.1) | + +### Tropical Biomes + +#### tropical_rainforest + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | medium (0.3), small (0.25) | large (0.2), tiny (0.15) | huge (0.1) | +| diet | herbivore (0.25), omnivore (0.25) | carnivore (0.2), detritivore (0.15) | producer (0.1), filter_feeder (0.05) | +| habitat | arboreal (0.35), terrestrial (0.35) | | aerial (0.2), amphibious (0.1) | +| locomotion | climbing (0.3), walking (0.25) | flying (0.2) | slithering (0.15), burrowing (0.1) | +| reproduction | r_strategy (0.5) | k_strategy (0.5) | | +| thermal | warm_blooded (0.5) | cold_blooded (0.5) | | +| social | solitary (0.25), pack (0.2) | herd (0.2), swarm (0.2) | colony (0.15) | + +#### tropical_dry_forest + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | medium (0.3), small (0.3) | large (0.2) | tiny (0.1), huge (0.1) | +| diet | herbivore (0.3), omnivore (0.25) | carnivore (0.2) | detritivore (0.15), producer (0.1) | +| habitat | terrestrial (0.45), arboreal (0.25) | | aerial (0.2), amphibious (0.1) | +| locomotion | walking (0.35), climbing (0.2) | flying (0.2) | slithering (0.15), burrowing (0.1) | +| reproduction | r_strategy (0.55) | k_strategy (0.45) | | +| thermal | warm_blooded (0.45) | cold_blooded (0.55) | | +| social | herd (0.25), solitary (0.25) | pack (0.2) | swarm (0.15), colony (0.15) | + +#### savanna + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | large (0.3), medium (0.3) | small (0.2) | huge (0.1), tiny (0.1) | +| diet | herbivore (0.35), carnivore (0.25) | omnivore (0.2) | detritivore (0.1), producer (0.1) | +| habitat | terrestrial (0.7) | | aerial (0.2), amphibious (0.1) | +| locomotion | walking (0.6) | flying (0.2) | burrowing (0.1), slithering (0.1) | +| reproduction | k_strategy (0.5) | r_strategy (0.5) | | +| thermal | warm_blooded (0.6) | cold_blooded (0.4) | | +| social | herd (0.35), pack (0.25) | solitary (0.2) | swarm (0.1), colony (0.1) | + +#### desert + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | small (0.35), tiny (0.3) | medium (0.2) | large (0.1), huge (0.05) | +| diet | omnivore (0.3), carnivore (0.25) | herbivore (0.2), detritivore (0.15) | producer (0.1) | +| habitat | terrestrial (0.7) | subterranean (0.15) | aerial (0.15) | +| locomotion | walking (0.3), burrowing (0.25) | slithering (0.25) | flying (0.15), climbing (0.05) | +| reproduction | r_strategy (0.65) | k_strategy (0.35) | | +| thermal | cold_blooded (0.7) | warm_blooded (0.3) | | +| social | solitary (0.45), colony (0.2) | pack (0.15) | swarm (0.15), herd (0.05) | + +### Temperate Biomes + +#### temperate_forest + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | medium (0.3), small (0.25) | large (0.2) | tiny (0.1), huge (0.15) | +| diet | herbivore (0.3), omnivore (0.25) | carnivore (0.2) | detritivore (0.15), producer (0.1) | +| habitat | terrestrial (0.5), arboreal (0.2) | | aerial (0.2), amphibious (0.1) | +| locomotion | walking (0.4) | climbing (0.2), flying (0.2) | burrowing (0.1), slithering (0.1) | +| reproduction | k_strategy (0.5) | r_strategy (0.5) | | +| thermal | warm_blooded (0.65) | cold_blooded (0.35) | | +| social | solitary (0.25), pack (0.25) | herd (0.2) | colony (0.15), swarm (0.15) | + +#### temperate_grassland + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | medium (0.3), small (0.25) | large (0.2) | tiny (0.1), huge (0.15) | +| diet | herbivore (0.35), carnivore (0.25) | omnivore (0.2) | detritivore (0.1), producer (0.1) | +| habitat | terrestrial (0.7) | | aerial (0.2), amphibious (0.1) | +| locomotion | walking (0.5) | flying (0.2), burrowing (0.15) | slithering (0.1), climbing (0.05) | +| reproduction | r_strategy (0.5) | k_strategy (0.5) | | +| thermal | warm_blooded (0.6) | cold_blooded (0.4) | | +| social | herd (0.35), pack (0.25) | solitary (0.2) | swarm (0.1), colony (0.1) | + +#### chaparral + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | small (0.35), medium (0.3) | tiny (0.15) | large (0.15), huge (0.05) | +| diet | omnivore (0.3), herbivore (0.25) | carnivore (0.2) | detritivore (0.15), producer (0.1) | +| habitat | terrestrial (0.7) | | aerial (0.15), subterranean (0.15) | +| locomotion | walking (0.35), burrowing (0.2) | slithering (0.2) | flying (0.15), climbing (0.1) | +| reproduction | r_strategy (0.6) | k_strategy (0.4) | | +| thermal | cold_blooded (0.55) | warm_blooded (0.45) | | +| social | solitary (0.35), pack (0.2) | colony (0.2) | herd (0.15), swarm (0.1) | + +#### swamp + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | medium (0.3), small (0.25) | large (0.2), tiny (0.15) | huge (0.1) | +| diet | omnivore (0.25), carnivore (0.25) | herbivore (0.2), detritivore (0.2) | filter_feeder (0.1) | +| habitat | amphibious (0.4), terrestrial (0.3) | | aquatic (0.15), aerial (0.15) | +| locomotion | swimming (0.3), walking (0.25) | slithering (0.2) | flying (0.15), burrowing (0.1) | +| reproduction | r_strategy (0.6) | k_strategy (0.4) | | +| thermal | cold_blooded (0.6) | warm_blooded (0.4) | | +| social | solitary (0.3), colony (0.2) | swarm (0.2), pack (0.15) | herd (0.15) | + +#### bog + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | tiny (0.35), small (0.35) | medium (0.2) | large (0.08), huge (0.02) | +| diet | detritivore (0.3), herbivore (0.25) | omnivore (0.2) | carnivore (0.15), filter_feeder (0.1) | +| habitat | amphibious (0.35), terrestrial (0.35) | | aerial (0.2), aquatic (0.1) | +| locomotion | walking (0.25), swimming (0.2) | flying (0.2), burrowing (0.15) | slithering (0.15), climbing (0.05) | +| reproduction | r_strategy (0.7) | k_strategy (0.3) | | +| thermal | cold_blooded (0.6) | warm_blooded (0.4) | | +| social | swarm (0.3), colony (0.25) | solitary (0.2) | herd (0.15), pack (0.1) | + +### Cold Biomes + +#### boreal_forest + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | medium (0.3), large (0.25) | small (0.2) | huge (0.15), tiny (0.1) | +| diet | herbivore (0.3), carnivore (0.25) | omnivore (0.25) | detritivore (0.1), producer (0.1) | +| habitat | terrestrial (0.6) | arboreal (0.15) | aerial (0.15), amphibious (0.1) | +| locomotion | walking (0.5) | climbing (0.15), flying (0.15) | burrowing (0.1), slithering (0.1) | +| reproduction | k_strategy (0.55) | r_strategy (0.45) | | +| thermal | warm_blooded (0.75) | cold_blooded (0.25) | | +| social | pack (0.3), solitary (0.25) | herd (0.2) | colony (0.15), swarm (0.1) | + +#### tundra + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | large (0.3), medium (0.3) | small (0.2) | huge (0.1), tiny (0.1) | +| diet | herbivore (0.35), carnivore (0.25) | omnivore (0.2) | detritivore (0.15), producer (0.05) | +| habitat | terrestrial (0.8) | | aerial (0.15), amphibious (0.05) | +| locomotion | walking (0.6) | flying (0.2) | burrowing (0.1), slithering (0.05), climbing (0.05) | +| reproduction | k_strategy (0.6) | r_strategy (0.4) | | +| thermal | warm_blooded (0.85) | cold_blooded (0.15) | | +| social | herd (0.4), pack (0.25) | solitary (0.2) | colony (0.1), swarm (0.05) | + +#### polar_desert + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | medium (0.3), small (0.3) | large (0.2) | tiny (0.15), huge (0.05) | +| diet | carnivore (0.3), herbivore (0.25) | omnivore (0.2) | detritivore (0.15), filter_feeder (0.1) | +| habitat | terrestrial (0.5), aquatic (0.3) | | amphibious (0.2) | +| locomotion | walking (0.35), swimming (0.3) | | flying (0.15), burrowing (0.1), slithering (0.1) | +| reproduction | k_strategy (0.65) | r_strategy (0.35) | | +| thermal | warm_blooded (0.9) | cold_blooded (0.1) | | +| social | herd (0.3), solitary (0.3) | pack (0.2) | colony (0.15), swarm (0.05) | + +### Elevation Biomes + +#### montane_forest + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | medium (0.3), large (0.25) | small (0.2) | huge (0.15), tiny (0.1) | +| diet | herbivore (0.3), omnivore (0.25) | carnivore (0.2) | detritivore (0.15), producer (0.1) | +| habitat | terrestrial (0.5), arboreal (0.2) | | aerial (0.2), amphibious (0.1) | +| locomotion | walking (0.35), climbing (0.25) | flying (0.2) | burrowing (0.1), slithering (0.1) | +| reproduction | k_strategy (0.55) | r_strategy (0.45) | | +| thermal | warm_blooded (0.7) | cold_blooded (0.3) | | +| social | solitary (0.3), pack (0.25) | herd (0.2) | colony (0.15), swarm (0.1) | + +#### cloud_forest + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | small (0.3), medium (0.3) | tiny (0.2) | large (0.15), huge (0.05) | +| diet | herbivore (0.25), omnivore (0.25) | detritivore (0.2) | carnivore (0.15), producer (0.15) | +| habitat | arboreal (0.4), terrestrial (0.3) | | aerial (0.2), amphibious (0.1) | +| locomotion | climbing (0.35), flying (0.2) | walking (0.2) | slithering (0.15), burrowing (0.1) | +| reproduction | r_strategy (0.5) | k_strategy (0.5) | | +| thermal | warm_blooded (0.5) | cold_blooded (0.5) | | +| social | solitary (0.3), colony (0.2) | swarm (0.2) | pack (0.15), herd (0.15) | + +#### alpine_meadow + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | medium (0.3), small (0.25) | large (0.2) | tiny (0.15), huge (0.1) | +| diet | herbivore (0.4), omnivore (0.2) | carnivore (0.2) | detritivore (0.1), producer (0.1) | +| habitat | terrestrial (0.7) | | aerial (0.25), amphibious (0.05) | +| locomotion | walking (0.45), climbing (0.2) | flying (0.2) | burrowing (0.1), slithering (0.05) | +| reproduction | k_strategy (0.55) | r_strategy (0.45) | | +| thermal | warm_blooded (0.75) | cold_blooded (0.25) | | +| social | herd (0.3), solitary (0.25) | pack (0.25) | colony (0.1), swarm (0.1) | + +#### alpine_tundra + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | medium (0.3), small (0.25) | large (0.2) | tiny (0.15), huge (0.1) | +| diet | herbivore (0.35), carnivore (0.25) | omnivore (0.2) | detritivore (0.15), producer (0.05) | +| habitat | terrestrial (0.8) | | aerial (0.15), subterranean (0.05) | +| locomotion | walking (0.4), climbing (0.25) | flying (0.15) | burrowing (0.15), slithering (0.05) | +| reproduction | k_strategy (0.6) | r_strategy (0.4) | | +| thermal | warm_blooded (0.8) | cold_blooded (0.2) | | +| social | herd (0.3), pack (0.25) | solitary (0.25) | colony (0.15), swarm (0.05) | + +#### permanent_ice + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | small (0.3), medium (0.3) | tiny (0.2) | large (0.15), huge (0.05) | +| diet | carnivore (0.3), herbivore (0.25) | omnivore (0.2) | detritivore (0.15), producer (0.1) | +| habitat | terrestrial (0.5), aquatic (0.3) | | amphibious (0.2) | +| locomotion | walking (0.3), swimming (0.3) | | flying (0.15), climbing (0.15), burrowing (0.1) | +| reproduction | k_strategy (0.7) | r_strategy (0.3) | | +| thermal | warm_blooded (0.9) | cold_blooded (0.1) | | +| social | herd (0.3), solitary (0.3) | pack (0.2) | colony (0.15), swarm (0.05) | + +### Subterranean Biome + +#### subterranean + +| Trait | High Weight | Medium | Low | +|-------|-----------|---------|----| +| size | small (0.35), tiny (0.3) | medium (0.2) | large (0.1), huge (0.05) | +| diet | omnivore (0.25), detritivore (0.25) | carnivore (0.2), herbivore (0.15) | producer (0.1), filter_feeder (0.05) | +| habitat | subterranean (0.8) | | amphibious (0.1), terrestrial (0.1) | +| locomotion | burrowing (0.35), climbing (0.3) | walking (0.15) | slithering (0.15), swimming (0.05) | +| reproduction | r_strategy (0.65) | k_strategy (0.35) | | +| thermal | cold_blooded (0.75) | warm_blooded (0.25) | | +| social | colony (0.35), swarm (0.2) | solitary (0.2) | pack (0.15), herd (0.1) | + +Typical generated species: +- small + burrowing + cold_blooded + colony + omnivore = "cave insect-like" +- medium + climbing + cold_blooded + solitary + carnivore = "giant spider-like" +- small + flying + warm_blooded + swarm + carnivore = "bat-like" +- tiny + burrowing + cold_blooded + colony + detritivore = "cave worm-like" +- medium + swimming + cold_blooded + solitary + carnivore = "blind cave fish-like" (in flooded caves) + +--- + +## Quality Tier Derivation + +### Formula + +``` +species_quality = clamp(base + modifiers, 1, 5) + +base = SIZE_QUALITY[size] + tiny → 1 + small → 2 + medium → 3 + large → 4 + huge → 5 + +modifiers: + r_strategy → -1 + k_strategy → 0 + detritivore → -1 + carnivore → +1 +``` + +### Quality Range (species lifetime) + +Species don't stay at one quality. They have a quality range based on the derived base: + +| Derived Quality | Quality Range | Example | +|----------------|-------------|---------| +| Q1 | Q1-Q2 | Worms, algae, tiny insects | +| Q2 | Q1-Q3 | Common fish, rabbits, cave insects | +| Q3 | Q2-Q4 | Deer, wolves (young), standard predators | +| Q4 | Q3-Q5 | Bears, sharks, dire wolves | +| Q5 | Q4-Q5 | Ancient leviathans, megafauna | + +An individual creature spawns at `quality_range[0]` and ages up toward `quality_range[1]`, capped by `min(species.max_quality, tile.quality)`. + +--- + +## Spawn Probability Table + +Tile quality gates which species quality tiers can spawn. Higher-quality tiles support rarer species. + +| Tile Quality | Q1 species | Q2 species | Q3 species | Q4 species | Q5 species | +|-------------|-----------|-----------|-----------|-----------|-----------| +| Q1 tile | 50% | 30% | 10% | 0% | 0% | +| Q2 tile | 30% | 40% | 25% | 5% | 0% | +| Q3 tile | 10% | 25% | 40% | 20% | 5% | +| Q4 tile | 5% | 15% | 30% | 35% | 15% | +| Q5 tile | 0% | 10% | 25% | 35% | 30% | + +Species can only spawn on tiles where `tile.quality >= species.min_quality`. The table above gives the probability of each quality tier being selected when the tile generates a new species. + +--- + +## Minimum Species Per Biome + +After generation across 10 seeds, each biome must produce: + +| Trophic Level | Minimum Count | Rationale | +|--------------|---------------|-----------| +| Producer | 3 | Base of food web, must be diverse enough to support herbivores | +| Herbivore | 3 | Multiple prey species prevent single-point food web collapse | +| Predator | 2 | At least one solitary + one pack predator for ecological balance | +| Detritivore | 1 | Recycler role, prevents dead-matter buildup | + +If any biome fails these minimums across the seed set, its trait weights need adjustment (increase weight for the underrepresented trophic level).