Prompt utilise pour regenerer cette page :
Page: Ecosystem Simulation
Description: "Observe predator-prey dynamics through natural selection and evolution"
Category: artificial-life
Icon: leaf
Tags: evolution, predator-prey, simulation, ecology
Status: new
Front matter (index.md):
title: "Ecosystem Simulation"
description: "Observe predator-prey dynamics through natural selection and evolution"
icon: "leaf"
tags: ["evolution", "predator-prey", "simulation", "ecology"]
status: ["new"]
HTML structure (index.md):
<section class="container visual size-800 ratio-1-1 canvas-contain">
<canvas id="ecosystem-canvas"></canvas>
</section>
Widget files:
- _stats.right.md (weight: 10): ##### Population — <div class="ecosystem-stats"> with:
- Plants: span.swatch.plant + span.value#stat-plants (initial "0")
- Herbivores: span.swatch.herbivore + span.value#stat-herbivores (initial "0")
- Carnivores: span.swatch.carnivore + span.value#stat-carnivores (initial "0")
- Generation: span.value#stat-generation (initial "0")
##### Herbivore Traits — <div class="ecosystem-stats traits"> with:
- Avg Speed: #stat-herb-speed (initial "-")
- Avg Size: #stat-herb-size (initial "-")
- Avg Sense: #stat-herb-sense (initial "-")
##### Carnivore Traits — <div class="ecosystem-stats traits"> with:
- Avg Speed: #stat-carn-speed (initial "-")
- Avg Size: #stat-carn-size (initial "-")
- Avg Sense: #stat-carn-sense (initial "-")
- _controls.right.md (weight: 20): ##### Controls
<div class="ecosystem-controls">
<div class="ecosystem-actions">
{{< button id="btn-start" label="Start" class="is-start" >}}
{{< button id="btn-pause" label="Pause" class="is-pause" >}}
{{< button id="btn-reset" label="Reset" >}}
</div>
</div>
##### Simulation — <div class="ecosystem-options">:
Speed: span#speed-value(1.0)x, range#opt-speed min=0.5 max=3 value=1 step=0.5
- _options.right.md (weight: 30): ##### Parameters — <div class="ecosystem-options"> with 6 sliders:
- Initial Plants: span#plants-value(80), range#opt-plants min=20 max=150 value=80 step=10
- Initial Herbivores: span#herbivores-value(30), range#opt-herbivores min=5 max=80 value=30 step=5
- Initial Carnivores: span#carnivores-value(10), range#opt-carnivores min=2 max=30 value=10 step=2
- Plant Growth: span#growth-value(15)%, range#opt-growth min=5 max=50 value=15 step=5 -> CONFIG.plantSpawnRate = value/100
- Reproduction: span#reproduction-value(120), range#opt-reproduction min=80 max=200 value=120 step=10
- Mutation: span#mutation-value(25)%, range#opt-mutation min=5 max=50 value=25 step=5 -> CONFIG.mutationRate = value/100
##### Effects — 4 checkboxes:
- checkbox#opt-traits: Show Traits
- checkbox#opt-energy: Show Energy
- checkbox#opt-obstacles: Obstacles
- checkbox#opt-zones: Fertility Zones
- _graph.right.md (weight: 40): ##### Population Over Time
<div class="ecosystem-graph">
<canvas id="population-graph"></canvas>
</div>
- _algorithm.after.md (no weight specified): Explains Lotka-Volterra dynamics. Species hierarchy: plants (green, grow randomly), herbivores (blue, eat plants), carnivores (red, hunt herbivores). Energy system: gain from food, deplete by metabolism, death at 0, reproduce at threshold. Evolution: speed/size/sense genes with mutation. Emergent population cycles.
Architecture (single file default.js):
IIFE, no imports (uses console.log)
CONFIG object:
initialPlants=80, initialHerbivores=30, initialCarnivores=10
plantSpawnRate=0.15, maxPlants=150, plantEnergy=30
herbivoreEnergyCost=0.08, carnivoreEnergyCost=0.12, preyEnergyTransfer=0.6
reproductionThreshold=120, reproductionCost=50
mutationRate=0.25, mutationStrength=0.15
speed=1.0
showTraits=false, showEnergy=false
obstaclesEnabled=false, zonesEnabled=false
HISTORY_LENGTH=300
State: canvas, ctx, graphCanvas, graphCtx, dpr. plants[], herbivores[], carnivores[], obstacles[], fertilityZones[]. populationHistory {plants[], herbivores[], carnivores[]}. isRunning, animationId, frameCount, generation, lastFrameTime, accumulator.
cachedColors: surface, plant('#27ae60'), herbivore('#3498db'), carnivore('#e74c3c'), grid, obstacle, zone.
Plant class:
constructor(x, y): position, size=4+random*3, energy=CONFIG.plantEnergy(30).
draw(ctx): green circle (cachedColors.plant).
Animal class (base):
constructor(x, y, genes=null, gen=0): position, generation. genes = [speed(0-1), size(0-1), sense(0-1)] random if null. Derived traits: speed=1+genes[0]*3, size=4+genes[1]*8, senseRange=30+genes[2]*100. energy = reproductionThreshold*0.8. Random vx/vy.
moveTowards(targetX, targetY): normalize direction, multiply by speed.
fleeFrom(threatX, threatY): normalize away direction, multiply by speed*1.2.
avoidObstacles(): for each obstacle, if within radius+size+20, push away proportional to (avoidDist-dist)/avoidDist * 2.
wander(): random acceleration +-0.25, limit to speed.
applyMovement(width, height): avoidObstacles if enabled. Apply velocity. Wrap edges (toroidal).
canReproduce(): energy > reproductionThreshold.
isDead(): energy <= 0.
createOffspringGenes(): per gene, mutationRate chance to mutate by +/- mutationStrength. Clamp 0-1.
getTraitColor(baseHue): HSL with saturation from speed gene (40+gene*50), lightness from size gene (60-gene*30).
drawEnergyBar(ctx): bar above entity. Width=size*2, height=3. Background rgba(0,0,0,0.3). Fill hue from red(0) to green(120) based on energy/threshold ratio.
Herbivore extends Animal:
update(width, height): first checks nearby carnivores (within senseRange) -> fleeFrom nearest. If not fleeing, seeks nearest plant (within senseRange) -> moveTowards. Else wander. applyMovement. Energy cost = herbivoreEnergyCost * (1 + speed_gene*0.5 + size_gene*0.3).
tryEat(): checks plants. If distance < this.size + plant.size, eats: gains plant.energy, removes plant. Returns boolean.
reproduce(): deducts reproductionCost. Creates child at +-10px offset with mutated genes. Tracks generation. Returns new Herbivore.
draw(ctx): if showTraits, getTraitColor(210) (blue). Else cachedColors.herbivore. Circle at size radius. Sense range faint stroke (alpha 0.1). Energy bar if showEnergy.
Carnivore extends Animal:
update(width, height): seeks nearest herbivore (within senseRange) -> moveTowards. Else wander. applyMovement. Energy cost = carnivoreEnergyCost * (1 + speed_gene*0.6 + size_gene*0.4).
tryEat(): checks herbivores. catchDistance = this.size + prey.size*0.5. Catch probability = 0.5 + (this.size/(prey.size+1))*0.3. Gains prey.energy * preyEnergyTransfer(0.6). Removes prey. Returns boolean.
reproduce(): same pattern as Herbivore. Returns new Carnivore.
draw(ctx): if showTraits, getTraitColor(0) (red). Else cachedColors.carnivore. Same pattern as herbivore draw.
Obstacle class:
constructor(x, y, radius). draw(ctx): gray semi-transparent circle with border.
FertilityZone class:
constructor(x, y, radius). contains(px, py): distance check. draw(ctx): green semi-transparent fill, dashed border (setLineDash [5,5]).
Utility functions:
cacheColors(): reads --background-color-surface, --draw-color-surface, --ecosystem-color-plant/herbivore/carnivore.
getWidth()/getHeight(): canvas buffer / dpr.
initCanvas(): main canvas 800x800 * dpr, ctx.setTransform. Graph canvas sized to parent element bounding rect * dpr.
createObstacles(): 4-6 obstacles, 10%-90% range, radius 20-50.
createFertilityZones(): 2-3 zones, 20%-80% range, radius 60-100.
collidesWithObstacle(x, y, radius): checks all obstacles.
isInFertilityZone(x, y): checks all zones.
spawnPlant(): if < maxPlants, random position (avoids obstacles), creates Plant.
recordHistory(): pushes counts to populationHistory, trims to HISTORY_LENGTH(300).
calculateAverageGenes(population): returns average [speed, size, sense] genes.
Main loop:
run(timestamp): fixed timestep accumulator pattern. accumulator += deltaTime * CONFIG.speed. fixedStep = 16.67ms. While accumulator >= fixedStep: updateSimulation(), accumulator -= fixedStep. draw(). requestAnimationFrame.
updateSimulation(): frameCount++. Plant spawning: spawnRate = plantSpawnRate. If zones enabled: spawnRate *= (1 + zones.length*0.5), extra spawns inside each zone (prob = plantSpawnRate*0.5, random angle/radius). Random < spawnRate -> spawnPlant. Update herbivores: update, tryEat, reproduce if canReproduce. Filter dead. Update carnivores: same pattern. Extinction protection: if herbivores=0 and carnivores>0, spawn 5 herbivores. If carnivores=0 and herbivores>10, spawn 2 carnivores. Record history every 5 frames. updateStats.
Drawing:
draw(): clear with surface. Draw fertility zones (behind), obstacles, plants, herbivores, carnivores. drawGraph().
drawGraph(): if graphCtx and history >= 2 points. Clear graph canvas. Find maxPop across all species. Draw axes (grid color, alpha 0.3). Draw 3 colored lines (plant/herbivore/carnivore). Padding 25px. x = i/(len-1), y = pop/maxPop scaled.
Stats:
updateStats(): updates stat-plants/herbivores/carnivores/generation. Herbivore traits: stat-herb-speed/size/sense (toFixed(2) or "-"). Carnivore traits: stat-carn-speed/size/sense.
updateValueDisplay(id, value): sets element textContent.
Controls:
toggleRun(): toggles isRunning, .is-running on .ecosystem-controls. If starting: lastFrameTime = performance.now(), accumulator=0, requestAnimationFrame. If stopping: cancelAnimationFrame.
reset(): stops if running, removes .is-running. Clears all populations. Resets generation/frameCount/history. Creates obstacles/zones if enabled. Spawns initial populations (avoids obstacles). updateStats, draw.
initControls(): btn-start (start if not running), btn-pause (pause if running), btn-reset (reset). Sliders: opt-speed -> CONFIG.speed (toFixed(1)). opt-plants/herbivores/carnivores -> CONFIG.initial*. opt-growth -> CONFIG.plantSpawnRate (value/100). opt-reproduction -> CONFIG.reproductionThreshold. opt-mutation -> CONFIG.mutationRate (value/100). Checkboxes: opt-traits -> showTraits, opt-energy -> showEnergy (redraw if paused). opt-obstacles -> obstaclesEnabled (create/clear). opt-zones -> zonesEnabled (create/clear).
Initialization:
init(): gets ecosystem-canvas + population-graph (optional). cacheColors, prefers-color-scheme listener. initCanvas. initControls. reset().
Auto-init: readyState check, DOMContentLoaded fallback.
SCSS file (default.scss):
:root CSS custom properties:
--ecosystem-color-plant: #27ae60
--ecosystem-color-herbivore: #3498db
--ecosystem-color-carnivore: #e74c3c
$breakpoint-mobile: 768px
layout-main scope:
main: flex column centered, padding 2rem, gap 1.5rem
.ecosystem-container: flex, justify center, 100% width
#ecosystem-canvas: 100% width/height, 1px border (draw-color-surface), 4px radius, surface bg
.ecosystem-controls:
.ecosystem-actions: flex row nowrap, justify center, gap 0.5rem
.is-start: display inline-flex (visible)
.is-pause: display none (hidden)
&.is-running: .is-start none, .is-pause inline-flex
.ecosystem-stats: flex column, gap 0.75rem
.stat-item: flex row, align center, space-between, gap 0.5rem
.label: flex, align center, gap 0.5rem, weight 300, surface text
.swatch: 0.75rem circle. &.plant/herbivore/carnivore: colored by CSS vars.
.value: 1.25rem, weight 600, tabular-nums, surface text
&.traits: gap 0.5rem, .label 0.85rem, .value 1rem weight 500
.ecosystem-graph: 100% width
#population-graph: 100% width, height 120px (100px mobile), 1px border, 4px radius, surface bg
.ecosystem-options: flex column, gap 0.75rem
.option-item: flex column, gap 0.25rem
label: 0.85rem, weight 400, surface text
input[type="range"]: 100% width, accent-color primary, pointer
.slider-row: flex row, align center, gap 0.5rem. input flex 1. .value min-width 3rem, right align, 0.875rem, tabular-nums.
&.checkbox: flex row, align center. label flex row, gap 0.5rem, pointer. checkbox pointer.
.ecosystem-legend: flex column, gap 0.5rem. &.inline: row wrap, gap 1rem.
.legend-item: inline-flex row, align center, gap 0.25rem, weight 300, surface text
.swatch: 0.75rem circle. &.plant/herbivore/carnivore: colored by CSS vars.
@media mobile: main padding 1rem gap 1rem. .ecosystem-controls flex-wrap. .stat-item .value 1rem. #population-graph height 100px.
Page entierement generee et maintenue par IA, sans intervention humaine.