Prompt utilisé pour régénérer cette page :
Page: Genetic Drift - Evolutionary Pathfinding
Title: "Genetic Drift"
Description: "Evolving creatures find the target through natural selection"
Icon: "dna"
Tags: ["genetic", "evolution", "optimization"]
Status: ["new"]
Category: artificial-intelligence
=== STRUCTURE ===
index.md front matter:
title: "Genetic Drift"
description: "Evolving creatures find the target through natural selection"
icon: "dna"
tags: ["genetic", "evolution", "optimization"]
status: ["new"]
index.md body:
<section class="container visual size-800 ratio-1-1 canvas-contain">
<canvas id="drift-canvas"></canvas>
</section>
Note: canvas uses tab indentation, not spaces.
=== WIDGET FILES ===
_stats.right.md (weight: 10, title: "Statistics"):
<dl class="drift-stats"> with 3 stat items:
- Generation (id="stat-generation", init "0")
- Best Fitness (id="stat-best-fitness", init "0")
- Avg Fitness (id="stat-avg-fitness", init "0")
_controls.right.md (weight: 20, title: "Controls"):
<div class="drift-controls"> with 3 icon buttons:
- {{< button id="btn-play" icon="play" aria="Play" class="is-start" >}}
- {{< button id="btn-play" icon="pause" aria="Pause" class="is-stop" >}}
- {{< button id="btn-reset" icon="refresh" aria="Reset" >}}
Note: both play/pause share id="btn-play" (visibility toggled via CSS)
Then two slider-group divs:
- Speed: <input type="range" id="slider-speed" min=1 max=10 value=1>
- Mutation: <input type="range" id="slider-mutation" min=0.1 max=5 step=0.1 value=1> + <span id="slider-mutation-val">1%</span>
_legend.after.md (weight: 10, title: "Legend"):
<div class="drift-legend inline"> with 4 legend items:
- Target (swatch class="target")
- Obstacle (swatch class="obstacle")
- High Fitness (swatch class="best")
- Low Fitness (swatch class="worst")
_source.after.md (weight: 90, title: "Algorithm"):
H3 "Genetic Algorithm" + 5 numbered steps:
1. DNA: 400 force vectors applied each frame
2. Fitness: inverse distance to target, bonuses/penalties
3. Selection: roulette wheel weighted by fitness
4. Crossover: single random cut point
5. Mutation: small chance per gene of random replacement
Closing paragraph about evolution of efficient paths.
=== JAVASCRIPT (default.js) ===
Import: panic from '/_lib/panic_v3.js'
Pattern: IIFE, strict mode.
Constants:
POPULATION_SIZE = 200, DNA_LENGTH = 400, CREATURE_RADIUS = 4
TARGET_RADIUS = 16, MAX_FORCE = 0.5, MAX_VELOCITY = 5, TRAIL_LENGTH = 5
DEFAULT_SPEED = 1, DEFAULT_MUTATION_RATE = 0.01
State: canvas, ctx, world (World instance), isRunning, animationId
Class Vector2D:
Properties: x, y
Methods: add(v) mutating, mult(n) mutating, limit(max) mutating, dist(v), copy()
Static: random(max) -> random vector in [-max, max] per component
Class DNA:
Constructor(genes=null): if null, generates DNA_LENGTH random Vector2D(MAX_FORCE)
crossover(partner): single random cut point, copies genes from each parent
mutate(mutationRate): per-gene probability of replacement with new random Vector2D(MAX_FORCE)
Class Creature:
Constructor(startPos, dna=null): startPos.copy(), pos, vel=(0,0), acc=(0,0),
dna (or new DNA), fitness=0, frameIndex=0, reachedTarget=false, hitObstacle=false,
finishFrame=DNA_LENGTH, trail=[]
update(target, obstacles, canvasWidth, canvasHeight):
Skip if reachedTarget, hitObstacle, or frameIndex >= DNA length.
Store trail position (max TRAIL_LENGTH), apply DNA gene as force to acc, inc frameIndex,
update vel=vel+acc (limited to MAX_VELOCITY), update pos=pos+vel, reset acc=(0,0),
boundary clamping (if x<CREATURE_RADIUS or x>width-CREATURE_RADIUS, clamp + reverse vel*-0.5),
same for y, check target collision (distance < CREATURE_RADIUS+TARGET_RADIUS),
check obstacle rect collision (AABB with creature radius)
evaluate(target): base fitness=1/(dist+1), if reached: *10 * (DNA_LENGTH/finishFrame),
if hit obstacle: *0.1, then square fitness (fitness*fitness)
show(ctx, maxFitness): normalized = fitness/maxFitness,
hue = 220 - normalized*100 (blue to green), hsla with 80% saturation 50% lightness 0.6 alpha,
draw trail dots with fading alpha, draw body circle
reset(startPos): fresh state, keep DNA
Class Population:
Constructor(size, startPos, mutationRate): create size random Creatures
evaluate(target): evaluate all, track bestFitness/avgFitness, store bestPath
selection(): roulette wheel proportional to fitness (total fitness sum, random spin)
reproduction(target): evaluate current, build new population via 2-parent crossover + mutation
allDone(): true if no creature still active (all reached/hit/exhausted)
Class World:
Constructor(canvas): get 2D context, resize canvas to parent (max height 600),
target = top center (width/2, TARGET_RADIUS+20),
obstacles = 3 horizontal bars forming maze: at 40% height (15%-45% width), 30% height (55%-85% width), 60% height (30%-70% width), all 20px tall,
startPos = bottom center (width/2, height-30),
speed = DEFAULT_SPEED, mutationRate = DEFAULT_MUTATION_RATE,
create Population, previousBestPath=[]
resize(): match parent clientWidth, height = min(width*0.75, 600)
createObstacles(): returns array of 3 {x, y, w, h} obstacle rects
step(): if allDone -> store previousBestPath, update mutationRate, call reproduction, updateStats;
else update all creatures with target, obstacles, canvas dimensions
run(): for speed iterations call step(), draw(), requestAnimationFrame if running
draw(): clear with CSS bg color, draw obstacles (fill rects with secondary color),
draw all creatures via show(ctx, maxFitness), draw previous best path (green dashed line),
draw target (red filled circle with white crosshair lines),
draw start position marker
updateStats(): update DOM for stat-generation, stat-best-fitness (toFixed(6)), stat-avg-fitness (toFixed(6))
init():
Get canvas #drift-canvas, create World, bind controls:
btn-play -> toggle play/pause (toggle .is-running class on .drift-controls container)
btn-reset -> create new World + initial draw
slider-speed input -> world.speed = parseInt(value)
slider-mutation input -> world.mutationRate = value/100, update span text with "%"
Initial draw call
=== SCSS (default.scss) ===
CSS variables (:root):
--drift-color-target: var(--color-danger)
--drift-color-best: var(--color-success)
--drift-color-obstacle: var(--text-color-secondary)
#drift-canvas: width/height 100%, background --background-color-surface
layout-main .drift-stats: flex center gap 2rem, dt 0.75rem uppercase --text-color-secondary, dd margin 0 1.5rem bold tabular-nums --text-color-primary
.drift-controls: flex row, gap 0.5rem, margin-top 1rem
.is-start: display block; .is-stop: display none
&.is-running: .is-start none, .is-stop block
.slider-group: flex center gap 0.5rem
label: 0.85rem weight 300 --text-color-secondary
input[type="range"]: accent-color --draw-color-primary, cursor pointer
.drift-legend: flex column gap 0.5rem, &.inline flex row gap 1rem
.legend-item: inline-flex gap 0.25rem, weight 300 --text-color-secondary
.swatch: 0.75rem circle
&.target -> --drift-color-target
&.best -> --drift-color-best
&.obstacle -> --drift-color-obstacle
=== STATE MANAGEMENT ===
No localStorage, sessionStorage, or URL params. All state in JS variables.
Keyboard: None. Mouse: None on canvas.
Canvas sizing: dynamic from parent (not fixed 800x800), max height 600px.
Page entièrement générée et maintenue par IA, sans intervention humaine.