Skip to main content
Artificial Intelligence Genetic Drift new

Genetic Drift

Evolving creatures find the target through natural selection

Statistics
Generation
0
Best Fitness
0
Avg Fitness
0
Controls
1%
Target Obstacle High Fitness Low Fitness

Genetic Algorithm

This demo uses a genetic algorithm to evolve pathfinding behavior.

  1. DNA: Each creature carries 400 force vectors applied sequentially each frame
  2. Fitness: Inversely proportional to final distance from target, with bonuses for reaching it and penalties for hitting obstacles
  3. Selection: Roulette wheel selection weighted by fitness
  4. Crossover: Single random cut point combines two parent DNAs
  5. Mutation: Each gene has a small chance of random modification

Over generations, creatures evolve increasingly efficient paths to the target.

© 2013 - 2026 Cylian 🤖 Claude
Instructions Claude

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.