Try   HackMD

Digitale Evolution mit genetischem Algorithmus

<!--
  MIT License
  Copyright (c) 2025 Rob de Roy | Wolfram Consult GmbH & Co. KG

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
-->
<!DOCTYPE html>
<html lang="de">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Genetischer Algorithmus</title>
  <style>
    /* Allgemeine Stile */
    body {
      margin: 0;
      padding: 0;
      font-family: Arial, sans-serif;
      color: white;
      background-color: #121212;
      overflow: auto; /* Erlaube Scrollen, wenn der Inhalt zu groß wird */
    }

    /* Partikelanimation */
    canvas {
      position: fixed; /* Animation bleibt fixiert im Hintergrund */
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      pointer-events: none;
      z-index: -1; /* Stelle sicher, dass die Animation hinter dem Content bleibt */
    }

    /* Hauptcontainer */
    .container {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      padding: 20px; /* Füge Padding hinzu, um den Content vom Rand abzurücken */
      max-width: 800px; /* Begrenze die Breite des Containers */
      margin: 0 auto; /* Zentriere den Container horizontal */
    }

    h1 {
      font-size: 3em;
      margin: 0 0 20px;
      text-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
    }

    p.explanation {
      font-size: 1.2em;
      margin: 0 0 20px;
      max-width: 600px;
      text-align: center;
      line-height: 1.6;
    }

    input[type="text"], input[type="number"] {
      padding: 15px;
      width: 400px;
      font-size: 18px;
      border: none;
      border-radius: 5px;
      margin-bottom: 20px;
      background-color: rgba(255, 255, 255, 0.1);
      color: white;
      box-shadow: 0 0 10px rgba(255, 255, 255, 0.3);
    }

    button {
      padding: 10px 20px;
      font-size: 16px;
      color: white;
      border: none;
      border-radius: 5px;
      cursor: pointer;
      transition: transform 0.3s ease, box-shadow 0.3s ease;
      margin: 5px;
    }

    button.start {
      background-color: #4CAF50;
      box-shadow: 0 0 10px rgba(76, 175, 80, 0.5);
    }

    button.reset {
      background-color: #f44336;
      box-shadow: 0 0 10px rgba(244, 67, 54, 0.5);
    }

    button.speed {
      background-color: #2196F3;
      box-shadow: 0 0 10px rgba(33, 150, 243, 0.5);
    }

    label {
      font-size: 16px;
      margin-right: 10px;
    }

    input[type="checkbox"] {
      transform: scale(1.5);
      margin-right: 10px;
    }

    button:hover {
      transform: scale(1.1);
      box-shadow: 0 0 20px rgba(255, 255, 255, 0.8);
    }

    #output {
      margin-top: 20px;
      width: 100%; /* Mache das Ausgabefeld responsive */
      max-width: 600px; /* Begrenze die maximale Breite */
      height: 300px;
      overflow-y: auto;
      border: 2px solid rgba(255, 255, 255, 0.3);
      padding: 10px;
      background-color: rgba(0, 0, 0, 0.5);
      font-family: Courier, monospace;
      font-size: 14px;
      color: white;
      scrollbar-width: thin;
      scrollbar-color: rgba(255, 255, 255, 0.5) transparent;
    }

    #info {
      margin-top: 10px;
      font-size: 18px;
      color: rgba(255, 255, 255, 0.8);
    }

    #bestPhrase {
      color: #4CAF50; /* Grüne Schriftfarbe für die beste Phrase */
      font-weight: bold;
    }

    #totalYears {
      color: #FFC107; /* Gelbe Schriftfarbe für die Gesamtdauer */
      font-weight: bold;
    }
  </style>
</head>
<body>
  <!-- Partikelanimation -->
  <canvas id="particleCanvas"></canvas>

  <!-- Hauptinhalt -->
  <div class="container">
    <h1>Genetischer Algorithmus</h1>
    <p class="explanation">
      Willkommen beim <strong>Genetischen Algorithmus-Tool</strong>! Dieses Tool simuliert den Evolutionsprozess, um aus einer zufälligen Phrase dein Ziel zu erreichen. Dabei verwendet es Prinzipien wie Mutation, Selektion und Kreuzung, um nach und nach die richtige Lösung zu finden.
      
      <br>
    </p>
    <input type="text" id="targetInput" placeholder="Gib dein Ziel ein..." />
    <label for="populationSize">Populationgröße (50–500): </label>
    <input type="number" id="populationSize" min="50" max="500" value="300" />
    <label><input type="checkbox" id="eliteMode" checked> Elite behalten</label>
    <div>
      <button class="start" onclick="startEvolution()">Start Evolution</button>
      <button class="reset" onclick="resetEvolution()">Reset</button>
      <button class="speed" onclick="changeSpeed(-1)">Verlangsamen</button>
      <button class="speed" onclick="changeSpeed(1)">Beschleunigen</button>
    </div>

    <div id="info">
      Generation: <span id="generation">0</span><br>
      Beste Phrase: <span id="bestPhrase"></span><br>
      Gesamtdauer: <span id="totalYears">0 Jahre</span>
    </div>
    <div id="output"></div>
  </div>

  <script>
    // Partikelanimation
    const canvas = document.getElementById("particleCanvas");
    const ctx = canvas.getContext("2d");

    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    window.addEventListener("resize", () => {
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;
    });

    class Particle {
      constructor(x, y) {
        this.x = x;
        this.y = y;
        this.size = Math.random() * 2 + 1;
        this.color = `rgba(255, 255, 255, ${Math.random() * 0.5 + 0.1})`;
        this.velocity = { x: (Math.random() - 0.5) * 2, y: (Math.random() - 0.5) * 2 };
        this.gravity = 0.05;
      }

      update() {
        this.velocity.y += this.gravity;
        this.x += this.velocity.x;
        this.y += this.velocity.y;

        if (this.y + this.size > canvas.height) {
          this.velocity.y *= -0.5;
        }
        if (this.x + this.size > canvas.width || this.x - this.size < 0) {
          this.velocity.x *= -1;
        }
      }

      draw() {
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
        ctx.fillStyle = this.color;
        ctx.fill();
        ctx.closePath();
      }
    }

    let particles = [];
    function initParticles() {
      for (let i = 0; i < 100; i++) {
        particles.push(new Particle(Math.random() * canvas.width, Math.random() * canvas.height));
      }
    }

    function animateParticles() {
      requestAnimationFrame(animateParticles);
      ctx.clearRect(0, 0, canvas.width, canvas.height);

      particles.forEach((particle, index) => {
        particle.update();
        particle.draw();

        // Verbinde Partikel
        particles.forEach((otherParticle, otherIndex) => {
          if (index !== otherIndex) {
            const dx = particle.x - otherParticle.x;
            const dy = particle.y - otherParticle.y;
            const distance = Math.sqrt(dx * dx + dy * dy);
            if (distance < 100) {
              ctx.strokeStyle = `rgba(255, 255, 255, ${1 - distance / 100})`;
              ctx.lineWidth = 0.5;
              ctx.beginPath();
              ctx.moveTo(particle.x, particle.y);
              ctx.lineTo(otherParticle.x, otherParticle.y);
              ctx.stroke();
            }
          }
        });
      });
    }

    initParticles();
    animateParticles();

    // Genetischer Algorithmus
    const charset = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~äöüßÄÖÜ"; // Erweiterter Zeichensatz

    let mutationRate = 0.02;
    let populationSize = 300; // Standardwert
    let population = [];
    let target = "";
    let generation = 0;
    let evolutionInterval = null; // Für Geschwindigkeitssteuerung
    let speedFactor = 1; // Standardgeschwindigkeit
    let bestPhrase = ""; // Best Phrase global deklarieren

    class GeneticDNA {
      constructor(numGenes) {
        this.genes = [];
        this.fitness = 0;
        for (let i = 0; i < numGenes; i++) {
          this.genes[i] = charset[Math.floor(Math.random() * charset.length)];
        }
      }

      calculateFitness(target) {
        let score = 0;
        for (let i = 0; i < this.genes.length; i++) {
          if (this.genes[i] === target[i]) {
            score++;
          }
        }
        this.fitness = score / target.length;
      }

      crossover(partner) {
        let child = new GeneticDNA(this.genes.length);
        let midpoint = Math.floor(Math.random() * this.genes.length);
        for (let i = 0; i < this.genes.length; i++) {
          if (i < midpoint) {
            child.genes[i] = this.genes[i];
          } else {
            child.genes[i] = partner.genes[i];
          }
        }
        return child;
      }

      mutate(mutationRate) {
        for (let i = 0; i < this.genes.length; i++) {
          if (Math.random() < mutationRate) {
            this.genes[i] = charset[Math.floor(Math.random() * charset.length)];
          }
        }
      }

      getPhrase() {
        return this.genes.join("");
      }
    }

    function startEvolution() {
      if (evolutionInterval) return; // Verhindert mehrere gleichzeitige Evolutionsprozesse

      target = document.getElementById("targetInput").value;
      if (!target) {
        alert("Bitte gib ein Ziel ein!");
        return;
      }

      // Lese die Populationgröße aus dem Eingabefeld
      populationSize = parseInt(document.getElementById("populationSize").value, 10);
      if (isNaN(populationSize) || populationSize < 50 || populationSize > 500) {
        alert("Die Populationgröße muss zwischen 50 und 500 liegen.");
        return;
      }

      population = [];
      for (let i = 0; i < populationSize; i++) {
        population[i] = new GeneticDNA(target.length);
      }
      generation = 0;
      updateUI();

      evolutionInterval = setInterval(evolve, 100 / speedFactor); // Anpassung an Geschwindigkeit
    }

    function evolve() {
      let highestFitness = 0;

      for (let phrase of population) {
        phrase.calculateFitness(target);
        if (phrase.fitness > highestFitness) {
          highestFitness = phrase.fitness;
          bestPhrase = phrase.getPhrase(); // Aktualisiere die globale Variable
        }
      }

      if (highestFitness === 1) {
        clearInterval(evolutionInterval);
        evolutionInterval = null;

        // Zeige das Ergebnis im Alert-Fenster
        const yearsPerGeneration = 30; // Durchschnittliche menschliche Generation
        const totalYears = generation * yearsPerGeneration;
        alert(`Ziel erreicht: "${target}"\nDie beteiligten Generationen hätten in der Realität ${totalYears} Jahre benötigt, um dieses Ziel zu erreichen.`);
      }

      let matingPool = [];
      for (let phrase of population) {
        let n = Math.floor(phrase.fitness * 100);
        for (let j = 0; j < n; j++) {
          matingPool.push(phrase);
        }
      }

      // Elitism-Option
      let eliteMode = document.getElementById("eliteMode").checked;
      let elites = [];
      if (eliteMode) {
        population.sort((a, b) => b.fitness - a.fitness); // Sortiere nach Fitness
        elites = population.slice(0, Math.ceil(populationSize * 0.1)); // Behalte die besten 10 % als Elite
      }

      for (let i = 0; i < population.length - elites.length; i++) {
        let partnerA = matingPool[Math.floor(Math.random() * matingPool.length)];
        let partnerB = matingPool[Math.floor(Math.random() * matingPool.length)];
        let child = partnerA.crossover(partnerB);
        child.mutate(mutationRate);
        population[i] = child;
      }

      // Füge die Elite zurück in die Population
      if (eliteMode) {
        population.splice(population.length - elites.length, 0, ...elites);
      }

      generation++;
      updateUI(); // UI mit bestPhrase und Gesamtdauer aktualisieren
    }

    function resetEvolution() {
      clearInterval(evolutionInterval);
      evolutionInterval = null;
      population = [];
      generation = 0;
      document.getElementById("targetInput").value = "";
      document.getElementById("populationSize").value = 300; // Setze Standardwert zurück
      document.getElementById("info").innerHTML = `
        Generation: <span id="generation">0</span><br>
        Beste Phrase: <span id="bestPhrase"></span><br>
        Gesamtdauer: <span id="totalYears">0 Jahre</span>
      `;
      document.getElementById("output").textContent = "";
    }

    function changeSpeed(direction) {
      speedFactor += direction * 0.5; // Ändere Geschwindigkeit in Schritten von 0.5
      speedFactor = Math.max(0.1, speedFactor); // Mindestgeschwindigkeit 0.1
      if (evolutionInterval) {
        clearInterval(evolutionInterval);
        evolutionInterval = setInterval(evolve, 100 / speedFactor); // Aktualisiere Intervalldauer
      }
    }

    function updateUI() {
      // Berechne die Gesamtdauer in Jahren
      const yearsPerGeneration = 30; // Durchschnittliche menschliche Generation
      const totalYears = generation * yearsPerGeneration;

      // Aktualisiere das UI
      document.getElementById("info").innerHTML = `
        Generation: <span id="generation">${generation}</span><br>
        Beste Phrase: <span id="bestPhrase">${bestPhrase || "noch keine Phrase"}</span><br>
        Gesamtdauer: <span id="totalYears">${totalYears} Jahre</span>
      `;

      // Aktualisiere das Ausgabefeld mit der aktuellen Population
      let output = document.getElementById("output");
      output.textContent = population.map(p => p.getPhrase()).join("\n");
    }
  </script>
</body>
  <br>
    <p style="text-align: center;"><span style="color:#FFFFFF">&copy;</span><a href="https://wolfram-consult.com"><span style="color:#FFFFFF">by Wolfram Consult GmbH&amp;Co. KG</span></a></p>
</html>