🎮ArcadeLab

LernCampus – Lernen & Schach

by RocketWave60
1174 lines41.7 KB
▶ Play
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>LernCampus – Lernen & Schach</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chess.js/1.4.0/chess.min.js"></script>

<style>
:root {
  --primary:#4f46e5;
  --primary-dark:#3730a3;
  --bg:#f3f5fb;
  --card:#fff;
  --text:#172033;
  --muted:#667085;
  --border:#e5e7eb;
  --green:#15803d;
  --red:#b91c1c;
}
*{box-sizing:border-box}
body{margin:0;font-family:system-ui,-apple-system,"Segoe UI",sans-serif;background:var(--bg);color:var(--text);padding-bottom:90px}
header{background:linear-gradient(135deg,#4338ca,#7c3aed);color:white;padding:22px 16px}
.header-inner{max-width:1100px;margin:auto;display:flex;align-items:center;justify-content:space-between;gap:12px}
header h1{margin:0;font-size:27px}
header p{margin:5px 0 0;opacity:.9}
.badge{background:#ffffff25;border:1px solid #ffffff55;padding:8px 12px;border-radius:99px;font-size:13px}
main{max-width:1100px;margin:auto;padding:20px 15px}
.panel{background:var(--card);border:1px solid var(--border);border-radius:18px;padding:20px;margin-bottom:18px;box-shadow:0 4px 18px #17203308}
h2,h3{margin-top:0}
.muted{color:var(--muted)}
button,select,input{font:inherit}
button{border:0;cursor:pointer;border-radius:11px;padding:11px 15px;font-weight:700;transition:background .15s}
button:disabled{opacity:.45;cursor:not-allowed}
.primary{background:var(--primary);color:white}
.primary:hover:not(:disabled){background:var(--primary-dark)}
.secondary{background:#eef0ff;color:#3730a3}
.neutral{background:#eef2f7;color:#273449}
.danger{background:#fee2e2;color:#991b1b}
select,input[type=text]{background:white;border:1px solid #cbd5e1;border-radius:10px;padding:11px;width:100%;min-width:0}
label{display:block;font-weight:700;margin-bottom:6px}
.hidden{display:none!important}
.filters{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;margin-bottom:18px}
.lesson-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(225px,1fr));gap:14px}
.lesson-card{background:white;border:1px solid var(--border);border-radius:16px;padding:17px;display:flex;flex-direction:column;gap:10px}
.lesson-card h3,.lesson-card p{margin:0}
.lesson-card p{color:var(--muted);font-size:14px}
.lesson-card button{margin-top:auto}
.subject-icon{font-size:28px;background:#eef0ff;width:52px;height:52px;border-radius:14px;display:grid;place-items:center}
.stats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}
.stat{background:#f8fafc;padding:14px;border-radius:13px;text-align:center}
.stat strong{display:block;font-size:25px}
.stat span{color:var(--muted);font-size:13px}
.action-row{display:flex;flex-wrap:wrap;gap:10px;margin-top:18px}
.learning-text{line-height:1.8;color:#374151}
.example{background:#f5f3ff;border-left:4px solid var(--primary);padding:14px;border-radius:9px;margin:16px 0}
.progress{height:8px;background:#e5e7eb;border-radius:999px;overflow:hidden;margin:15px 0}
.progress-bar{height:100%;width:0;background:var(--primary);transition:width .2s}
.question-box{padding:18px;background:#f8fafc;border-radius:14px;margin-top:12px}
.question-number{color:var(--primary);font-weight:800;margin-bottom:8px}
.answers{display:grid;gap:10px;margin-top:16px}
.answer-option{border:1px solid #cbd5e1;background:white;text-align:left;padding:14px;border-radius:12px;font-weight:600}
.answer-option.selected{background:#eef0ff;border-color:var(--primary)}
.feedback{margin-top:16px;padding:15px;border-radius:12px;white-space:pre-line;line-height:1.6;font-weight:600}
.feedback.correct{background:#dcfce7;color:#166534;border:1px solid #86efac}
.feedback.wrong{background:#fee2e2;color:#991b1b;border:1px solid #fca5a5}

/* Untere Navigation */
.bottom-nav{position:fixed;z-index:10;bottom:0;left:0;right:0;display:flex;justify-content:center;gap:12px;padding:12px 16px calc(12px + env(safe-area-inset-bottom));background:#ffffffed;backdrop-filter:blur(12px);border-top:1px solid var(--border)}
.nav-btn{min-width:130px;color:#475569;background:transparent;display:flex;justify-content:center;align-items:center;gap:8px}
.nav-btn.active{background:#eef0ff;color:var(--primary)}

/* Schach */
.chess-layout{display:grid;grid-template-columns:minmax(0,560px) minmax(220px,1fr);gap:20px;align-items:start}
.board{display:grid;grid-template-columns:repeat(8,1fr);width:100%;aspect-ratio:1;border:2px solid #6b513c;border-radius:5px;overflow:hidden;user-select:none}
.square{position:relative;display:grid;place-items:center;aspect-ratio:1;padding:0;border-radius:0;min-width:0;font-size:clamp(22px,5vw,48px);line-height:1}
.light{background:#f0d9b5}
.dark{background:#b58863}
.square.selected{box-shadow:inset 0 0 0 4px #facc15}
.square.lastmove{background-image:linear-gradient(#facc1560,#facc1560)}
.square.legal:after{content:"";width:25%;height:25%;background:#16653490;border-radius:50%;position:absolute}
.square.legal.capture:after{width:85%;height:85%;background:transparent;border:4px solid #16653490}
.piece-white{color:#fffdf4;text-shadow:1px 1px 2px #111,-1px -1px 1px #111}
.piece-black{color:#191919;text-shadow:0 1px 1px #fff6}
.square:focus-visible{outline:3px solid #4338ca;outline-offset:-3px}
.chess-controls{display:grid;gap:12px}
.status{padding:12px;border-radius:11px;background:#eef0ff;color:#3730a3;font-weight:700;line-height:1.5}
.move-list{background:#f8fafc;padding:12px;border-radius:12px;min-height:80px;max-height:180px;overflow:auto;font-size:14px;line-height:1.9;white-space:pre-wrap}
.chess-note{font-size:13px;color:var(--muted);line-height:1.6}
.setup-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}
.bot-label{display:inline-block;padding:5px 9px;background:#eef0ff;color:#3730a3;border-radius:99px;font-size:12px;font-weight:800;margin-bottom:10px}
#chessGame{margin-top:18px}
@media(max-width:720px){
  .header-inner{align-items:flex-start}
  header h1{font-size:22px}
  .badge{display:none}
  .filters,.setup-grid{grid-template-columns:1fr}
  .chess-layout{grid-template-columns:1fr}
  .panel{padding:15px}
  .nav-btn{min-width:0;flex:1}
}
</style>
</head>

<body>
<header>
  <div class="header-inner">
    <div>
      <h1>🎓 LernCampus</h1>
      <p>Lernen, üben und Schach spielen</p>
    </div>
    <div class="badge">Klassen 1–6 · 4 Fächer · Schach</div>
  </div>
</header>

<main>
  <!-- STARTSEITE -->
  <section id="learnPage">
    <div class="panel">
      <h2>👋 Willkommen!</h2>
      <p class="muted">Wähle deine Klasse und dein Fach. Löse Aufgaben Schritt für Schritt und sammle Punkte.</p>
      <div class="stats">
        <div class="stat"><strong id="statDone">0</strong><span>Abgeschlossene Lektionen</span></div>
        <div class="stat"><strong id="statPoints">0</strong><span>Punkte insgesamt</span></div>
        <div class="stat"><strong id="statRate">0%</strong><span>Richtige Antworten</span></div>
      </div>
    </div>

    <div class="panel">
      <h2>📚 Lektionen</h2>
      <div class="filters">
        <div>
          <label for="classFilter">Klasse</label>
          <select id="classFilter">
            <option value="all">Alle Klassen</option>
            <option value="1">Klasse 1</option><option value="2">Klasse 2</option>
            <option value="3">Klasse 3</option><option value="4">Klasse 4</option>
            <option value="5">Klasse 5</option><option value="6">Klasse 6</option>
          </select>
        </div>
        <div>
          <label for="subjectFilter">Fach</label>
          <select id="subjectFilter">
            <option value="all">Alle Fächer</option>
            <option value="Mathe">Mathe</option><option value="Deutsch">Deutsch</option>
            <option value="Englisch">Englisch</option><option value="Sachkunde">Sachkunde</option>
          </select>
        </div>
        <div>
          <label for="sortFilter">Sortierung</label>
          <select id="sortFilter">
            <option value="class">Nach Klasse</option>
            <option value="subject">Nach Fach</option>
            <option value="title">Nach Titel</option>
          </select>
        </div>
      </div>
      <div id="lessonGrid" class="lesson-grid"></div>
      <p id="emptyLessons" class="muted hidden">Für diese Auswahl gibt es keine Lektionen.</p>
    </div>
  </section>

  <!-- LEKTION -->
  <section id="lessonPage" class="hidden">
    <div class="panel">
      <button class="neutral leaveLesson">← Lektion verlassen</button>
      <div style="margin-top:18px">
        <span class="muted" id="lessonMeta"></span>
        <h2 id="lessonTitle"></h2>
        <div class="learning-text" id="lessonContent"></div>
        <div class="example" id="lessonExample"></div>
        <div class="action-row">
          <button class="primary" id="startQuiz">Aufgaben starten →</button>
        </div>
      </div>
    </div>
  </section>

  <!-- AUFGABEN -->
  <section id="quizPage" class="hidden">
    <div class="panel">
      <button class="neutral leaveLesson">← Lektion verlassen</button>
      <h2 id="quizTitle" style="margin-top:18px">📝 Aufgaben</h2>
      <p class="muted">Prüfe deine Antwort. Danach siehst du sofort, ob sie richtig oder falsch war.</p>
      <div class="progress"><div id="quizProgress" class="progress-bar"></div></div>
      <div id="questionArea"></div>
      <div id="questionFeedback" class="feedback hidden" aria-live="polite"></div>
      <div class="action-row">
        <button class="primary" id="checkAnswer">Antwort überprüfen</button>
        <button class="primary hidden" id="nextQuestion">Nächste Aufgabe →</button>
      </div>
    </div>
  </section>

  <!-- ERGEBNIS -->
  <section id="resultPage" class="hidden">
    <div class="panel">
      <button class="neutral leaveLesson">← Lektion verlassen</button>
      <h2 id="resultTitle" style="margin-top:18px">🎉 Geschafft!</h2>
      <p id="resultText"></p>
      <div class="stats">
        <div class="stat"><strong id="resultCorrect">0</strong><span>Richtig</span></div>
        <div class="stat"><strong id="resultTotal">0</strong><span>Aufgaben</span></div>
        <div class="stat"><strong id="resultPercent">0%</strong><span>Ergebnis</span></div>
      </div>
      <div class="action-row">
        <button class="primary" id="retryQuiz">Noch einmal üben</button>
        <button class="secondary leaveLesson">Andere Lektion wählen</button>
      </div>
    </div>
  </section>

  <!-- SCHACH -->
  <section id="chessPage" class="hidden">
    <div class="panel">
      <h2>♟️ Schach-Arena</h2>
      <p class="muted">Stelle deine Partie ein und drücke anschließend auf „Partie starten“.</p>

      <!-- Setup bleibt sichtbar, bevor das Spiel gestartet wird -->
      <div id="chessSetup">
        <div class="setup-grid">
          <div>
            <label for="botLevel">Bot-Stärke</label>
            <select id="botLevel">
              <option value="1">Stufe 1 – Anfänger</option>
              <option value="2">Stufe 2 – Leicht</option>
              <option value="3" selected>Stufe 3 – Mittel</option>
              <option value="4">Stufe 4 – Fortgeschritten</option>
              <option value="5">Stufe 5 – Stark</option>
              <option value="6">Stufe 6 – Experte</option>
            </select>
          </div>
          <div>
            <label for="humanColor">Deine Farbe</label>
            <select id="humanColor">
              <option value="w">Weiß – beginnt</option>
              <option value="b">Schwarz</option>
            </select>
          </div>
        </div>
        <div class="example" id="botDescription">
          <strong>Stufe 3 – Mittel</strong><br>Der Bot berücksichtigt Material, Zentrum und einfache Taktik.
        </div>
        <button class="primary" id="startChessGame">♟️ Partie starten</button>
      </div>

      <!-- Brett wird erst nach Partie starten angezeigt -->
      <div id="chessGame" class="hidden">
        <div class="chess-layout">
          <div>
            <div id="chessBoard" class="board" role="group" aria-label="Schachbrett"></div>
            <div class="action-row">
              <button id="undoChess" class="neutral">↶ Zug zurück</button>
              <button id="restartChess" class="secondary">↻ Partie neu starten</button>
              <button id="flipBoard" class="neutral">⇅ Brett drehen</button>
            </div>
            <div class="action-row">
              <button id="exitChess" class="danger">Partie beenden</button>
            </div>
          </div>
          <div class="chess-controls">
            <span class="bot-label" id="activeBotLabel">BOT · STUFE 3</span>
            <div id="chessStatus" class="status" aria-live="polite">Weiß ist am Zug.</div>
            <div>
              <h3>Letzte Züge</h3>
              <div id="moveList" class="move-list">Noch keine Züge.</div>
            </div>
            <p class="chess-note">
              Der Bot sucht nach möglichen Zügen, bewertet Figuren und Positionen und nutzt Alpha-Beta-Suche.
              Je höher die Stufe, desto tiefer sucht er voraus. Die höchste Stufe kann auf langsameren Geräten länger rechnen.
            </p>
          </div>
        </div>
      </div>
    </div>
  </section>
</main>

<!-- Diese Navigation wird in Lektion, Quiz und Ergebnis ausgeblendet -->
<nav class="bottom-nav" id="bottomNav" aria-label="Hauptnavigation">
  <button class="nav-btn active" id="navLearn">📚 Lernen</button>
  <button class="nav-btn" id="navChess">♟️ Schach</button>
</nav>

<script>
"use strict";

/* ============================
   LERNBEREICH
============================ */

const subjects = {
  Mathe: {
    icon:"➗",
    topics:["Plus und Minus","Zahlen verstehen","Einmaleins","Teilen","Geometrie","Sachaufgaben","Brüche","Größen und Messen","Dezimalzahlen","Rechnen mit Zahlen"]
  },
  Deutsch: {
    icon:"📖",
    topics:["Wörter und Buchstaben","Nomen und Artikel","Verben","Adjektive","Satzzeichen","Rechtschreibung","Satzbau","Wortarten","Lesen und Verstehen","Zeitformen"]
  },
  Englisch: {
    icon:"🇬🇧",
    topics:["Begrüßungen","Farben","Zahlen","Tiere","Familie","Essen und Trinken","Schule","Alltagssätze","Verben","Einfache Grammatik"]
  },
  Sachkunde: {
    icon:"🌍",
    topics:["Tiere","Pflanzen","Wetter","Unser Körper","Umwelt","Verkehr","Wasser","Jahreszeiten","Erde und Weltall","Gesund leben"]
  }
};

const descriptions = {
  Mathe:"Rechne, entdecke Zahlen und löse mathematische Rätsel.",
  Deutsch:"Trainiere Sprache, Grammatik, Rechtschreibung und Lesen.",
  Englisch:"Lerne englische Wörter und übe einfache Sätze.",
  Sachkunde:"Entdecke Natur, Umwelt, Körper und unsere Welt."
};

const lessonData=[];
let nextLessonId=1;

for(let grade=1;grade<=6;grade++){
  for(const subject of Object.keys(subjects)){
    subjects[subject].topics.forEach(topic=>{
      lessonData.push({
        id:nextLessonId++,
        grade,subject,topic,title:topic,
        icon:subjects[subject].icon,
        description:descriptions[subject]
      });
    });
  }
}

const STORAGE_KEY="lerncampus_v4";

function loadProgress(){
  try{
    return JSON.parse(localStorage.getItem(STORAGE_KEY))||{
      completed:[],points:0,attempts:0,correct:0
    };
  }catch{
    return {completed:[],points:0,attempts:0,correct:0};
  }
}

let progress=loadProgress();

function saveProgress(){
  try{localStorage.setItem(STORAGE_KEY,JSON.stringify(progress));}catch{}
}

function updateStats(){
  document.getElementById("statDone").textContent=progress.completed.length;
  document.getElementById("statPoints").textContent=progress.points;
  document.getElementById("statRate").textContent=
    (progress.attempts?Math.round(progress.correct/progress.attempts*100):0)+"%";
}

function markCompleted(id,score){
  if(!progress.completed.includes(id))progress.completed.push(id);
  progress.points+=score;
  saveProgress();
  updateStats();
}

const pages={
  learn:document.getElementById("learnPage"),
  lesson:document.getElementById("lessonPage"),
  quiz:document.getElementById("quizPage"),
  result:document.getElementById("resultPage"),
  chess:document.getElementById("chessPage")
};

function showPage(name){
  Object.values(pages).forEach(p=>p.classList.add("hidden"));
  pages[name].classList.remove("hidden");

  // Navigation nur außerhalb der eigentlichen Lektion und Aufgaben anzeigen.
  const showNavigation=(name==="learn"||name==="chess");
  document.getElementById("bottomNav").classList.toggle("hidden",!showNavigation);

  document.getElementById("navLearn").classList.toggle("active",name==="learn");
  document.getElementById("navChess").classList.toggle("active",name==="chess");

  if(name==="chess"&&chessStarted)renderBoard();
  window.scrollTo({top:0,behavior:"smooth"});
}

document.getElementById("navLearn").addEventListener("click",()=>showPage("learn"));
document.getElementById("navChess").addEventListener("click",()=>showPage("chess"));

function leaveLesson(){
  activeLesson=null;
  showPage("learn");
}
document.querySelectorAll(".leaveLesson").forEach(button=>{
  button.addEventListener("click",leaveLesson);
});

function renderLessons(){
  const grade=document.getElementById("classFilter").value;
  const subject=document.getElementById("subjectFilter").value;
  const sort=document.getElementById("sortFilter").value;

  const lessons=lessonData.filter(l=>
    (grade==="all"||String(l.grade)===grade)&&
    (subject==="all"||l.subject===subject)
  );

  lessons.sort((a,b)=>{
    if(sort==="subject")return a.subject.localeCompare(b.subject)||a.grade-b.grade;
    if(sort==="title")return a.title.localeCompare(b.title)||a.grade-b.grade;
    return a.grade-b.grade||a.subject.localeCompare(b.subject);
  });

  const grid=document.getElementById("lessonGrid");
  grid.innerHTML="";

  lessons.forEach(lesson=>{
    const done=progress.completed.includes(lesson.id);
    const card=document.createElement("article");
    card.className="lesson-card";

    const icon=document.createElement("div");
    icon.className="subject-icon";
    icon.textContent=lesson.icon;

    const meta=document.createElement("span");
    meta.className="muted";
    meta.textContent=`Klasse ${lesson.grade} · ${lesson.subject}`;

    const title=document.createElement("h3");
    title.textContent=lesson.title;

    const desc=document.createElement("p");
    desc.textContent=lesson.description;

    const button=document.createElement("button");
    button.className=done?"secondary":"primary";
    button.textContent=done?"Noch einmal üben ↻":"Lektion öffnen →";
    button.addEventListener("click",()=>openLesson(lesson.id));

    card.append(icon,meta,title,desc);
    if(done){
      const label=document.createElement("p");
      label.textContent="✅ Bereits abgeschlossen";
      card.append(label);
    }
    card.append(button);
    grid.append(card);
  });

  document.getElementById("emptyLessons").classList.toggle("hidden",lessons.length>0);
}

["classFilter","subjectFilter","sortFilter"].forEach(id=>{
  document.getElementById(id).addEventListener("change",renderLessons);
});

let activeLesson=null;
let questions=[];
let questionIndex=0;
let selectedOption=null;
let currentCorrect=0;
let alreadyChecked=false;

const englishWords=[
  ["cat","Katze"],["dog","Hund"],["book","Buch"],["house","Haus"],
  ["apple","Apfel"],["water","Wasser"],["school","Schule"],
  ["sun","Sonne"],["blue","blau"],["green","grün"],["mother","Mutter"],
  ["friend","Freund"],["bird","Vogel"],["milk","Milch"],["red","rot"]
];

const germanWords=[
  ["Hund","Nomen"],["laufen","Verb"],["schnell","Adjektiv"],
  ["Blume","Nomen"],["springen","Verb"],["freundlich","Adjektiv"],
  ["Tisch","Nomen"],["lachen","Verb"],["klein","Adjektiv"],
  ["lesen","Verb"],["Schule","Nomen"],["bunt","Adjektiv"]
];

const natureFacts=[
  {q:"Welcher Teil einer Pflanze nimmt meistens Wasser aus dem Boden auf?",a:"Die Wurzel",options:["Die Blüte","Die Wurzel","Das Blatt","Die Frucht"]},
  {q:"Was brauchen Pflanzen für die Fotosynthese?",a:"Licht, Wasser und Kohlendioxid",options:["Nur Sand","Nur Dunkelheit","Licht, Wasser und Kohlendioxid","Nur Wind"]},
  {q:"Welches Tier ist ein Säugetier?",a:"Der Delfin",options:["Der Delfin","Die Forelle","Der Frosch","Die Biene"]},
  {q:"In welchem Aggregatzustand ist Eis?",a:"Fest",options:["Gasförmig","Flüssig","Fest","Plasma"]},
  {q:"Welches Organ pumpt Blut durch den Körper?",a:"Das Herz",options:["Die Lunge","Das Herz","Der Magen","Die Haut"]},
  {q:"Wie heißt der Planet, auf dem wir leben?",a:"Erde",options:["Mars","Venus","Erde","Jupiter"]},
  {q:"Was entsteht, wenn Wasserdampf abkühlt?",a:"Wassertröpfchen",options:["Sand","Wassertröpfchen","Holz","Metall"]},
  {q:"Welche Energiequelle ist erneuerbar?",a:"Sonnenenergie",options:["Kohle","Erdöl","Sonnenenergie","Erdgas"]}
];

function makeQuestion(q,a,options=[],explanation=""){
  return {q,a:String(a),options:options.map(String),explanation};
}

function shuffle(arr){
  const a=arr.slice();
  for(let i=a.length-1;i>0;i--){
    const j=Math.floor(Math.random()*(i+1));
    [a[i],a[j]]=[a[j],a[i]];
  }
  return a;
}

function createMathQuestions(lesson){
  const result=[];
  for(let i=0;i<10;i++){
    const a=Math.floor(Math.random()*(lesson.grade*8+10))+1;
    const b=Math.floor(Math.random()*(lesson.grade*5+8))+1;
    const mode=(i+lesson.id)%4;
    let q,ans,explanation;

    if(mode===0){
      q=`Wie viel ist ${a} + ${b}?`;ans=a+b;explanation=`${a} + ${b} = ${ans}`;
    }else if(mode===1){
      const big=Math.max(a,b),small=Math.min(a,b);
      q=`Wie viel ist ${big} − ${small}?`;ans=big-small;explanation=`${big} − ${small} = ${ans}`;
    }else if(mode===2){
      const x=Math.min(12,Math.max(2,lesson.grade+i%4));
      const y=i%5+2;
      q=`Wie viel ist ${x} · ${y}?`;ans=x*y;explanation=`${x} · ${y} = ${ans}`;
    }else{
      const divisor=i%5+2;
      const resultValue=(i+2)*(lesson.grade%4+2);
      const dividend=divisor*resultValue;
      q=`Wie viel ist ${dividend} : ${divisor}?`;
      ans=dividend/divisor;explanation=`${dividend} : ${divisor} = ${ans}`;
    }
    result.push(makeQuestion(q,ans,[],explanation));
  }
  return result;
}

function createEnglishQuestions(){
  return shuffle(englishWords).slice(0,10).map((pair,i)=>{
    if(i%2===0){
      const wrong=shuffle(englishWords.filter(x=>x[1]!==pair[1])).slice(0,3).map(x=>x[1]);
      return makeQuestion(`Was bedeutet „${pair[0]}“ auf Deutsch?`,pair[1],shuffle([pair[1],...wrong]));
    }
    const wrong=shuffle(englishWords.filter(x=>x[0]!==pair[0])).slice(0,3).map(x=>x[0]);
    return makeQuestion(`Wie heißt „${pair[1]}“ auf Englisch?`,pair[0],shuffle([pair[0],...wrong]));
  });
}

function createGermanQuestions(lesson){
  const result=[];
  for(let i=0;i<10;i++){
    if((i+lesson.id)%2===0){
      const pair=germanWords[(i*3+lesson.id)%germanWords.length];
      const wrong=shuffle([...new Set(germanWords.map(x=>x[1]).filter(x=>x!==pair[1]))]).slice(0,2);
      result.push(makeQuestion(`Welche Wortart hat „${pair[0]}“?`,pair[1],shuffle([pair[1],...wrong]),`„${pair[0]}“ ist hier ein ${pair[1]}.`));
    }else{
      const words=[["Haus","das"],["Katze","die"],["Ball","der"],["Schule","die"],["Baum","der"],["Buch","das"]];
      const pair=words[(i+lesson.id)%words.length];
      result.push(makeQuestion(`Welcher Artikel passt zu „${pair[0]}“?`,pair[1],shuffle(["der","die","das"])));
    }
  }
  return result;
}

function createNatureQuestions(){
  return shuffle(natureFacts).map(f=>makeQuestion(f.q,f.a,f.options,f.a));
}

function createQuestions(lesson){
  if(lesson.subject==="Mathe")return shuffle(createMathQuestions(lesson));
  if(lesson.subject==="Englisch")return shuffle(createEnglishQuestions());
  if(lesson.subject==="Deutsch")return shuffle(createGermanQuestions(lesson));
  return createNatureQuestions();
}

function openLesson(id){
  activeLesson=lessonData.find(l=>l.id===id);
  if(!activeLesson)return;

  document.getElementById("lessonMeta").textContent=`Klasse ${activeLesson.grade} · ${activeLesson.subject}`;
  document.getElementById("lessonTitle").textContent=`${activeLesson.icon} ${activeLesson.title}`;

  const explanations={
    Mathe:"Rechne Schritt für Schritt. Schreibe Zwischenergebnisse auf und kontrolliere dein Ergebnis.",
    Deutsch:"Achte genau auf Wörter, ihre Bedeutung und ihre Funktion im Satz. Lies die Aufgabe sorgfältig.",
    Englisch:"Lerne neue Wörter zusammen mit ihrer Übersetzung. Lies die Wörter laut und bilde einfache Sätze.",
    Sachkunde:"Beobachte deine Umwelt und verbinde neue Informationen mit Beispielen aus deinem Alltag."
  };

  document.getElementById("lessonContent").innerHTML=
    `<p>${explanations[activeLesson.subject]}</p>
     <p>In dieser Lektion übst du das Thema <strong>${activeLesson.title}</strong>.
     Bearbeite die Aufgaben einzeln. Nach jeder Antwort erhältst du sofort eine Rückmeldung.</p>`;

  const examples={
    Mathe:"<strong>💡 Lerntipp</strong><br>Beispiel: 8 + 5 = 13. Rechne erst 8 + 2 = 10 und dann noch 3 dazu.",
    Deutsch:"<strong>💡 Lerntipp</strong><br>Nomen bezeichnen Menschen, Tiere oder Dinge. Verben beschreiben Tätigkeiten. Adjektive beschreiben Eigenschaften.",
    Englisch:"<strong>💡 Lerntipp</strong><br>„cat“ bedeutet „Katze“ und „book“ bedeutet „Buch“.",
    Sachkunde:"<strong>💡 Lerntipp</strong><br>Verbinde das Gelernte mit Dingen, die du in der Natur und im Alltag beobachten kannst."
  };

  document.getElementById("lessonExample").innerHTML=examples[activeLesson.subject];
  showPage("lesson");
}

document.getElementById("startQuiz").addEventListener("click",startQuiz);

function startQuiz(){
  if(!activeLesson)return;
  questions=createQuestions(activeLesson);
  questionIndex=0;
  currentCorrect=0;
  selectedOption=null;
  alreadyChecked=false;
  document.getElementById("quizTitle").textContent=`📝 ${activeLesson.title}`;
  showPage("quiz");
  renderQuestion();
}

function renderQuestion(){
  const area=document.getElementById("questionArea");
  const feedback=document.getElementById("questionFeedback");
  const q=questions[questionIndex];

  selectedOption=null;
  alreadyChecked=false;
  feedback.className="feedback hidden";
  feedback.textContent="";

  document.getElementById("checkAnswer").classList.remove("hidden");
  document.getElementById("checkAnswer").disabled=false;
  document.getElementById("nextQuestion").classList.add("hidden");
  document.getElementById("quizProgress").style.width=`${questionIndex/questions.length*100}%`;
  area.innerHTML="";

  const number=document.createElement("div");
  number.className="question-number";
  number.textContent=`Aufgabe ${questionIndex+1} von ${questions.length}`;

  const box=document.createElement("div");
  box.className="question-box";

  const title=document.createElement("h3");
  title.textContent=q.q;
  box.append(title);

  if(q.options.length){
    const answers=document.createElement("div");
    answers.className="answers";
    q.options.forEach(option=>{
      const button=document.createElement("button");
      button.type="button";
      button.className="answer-option";
      button.textContent=option;
      button.addEventListener("click",()=>{
        if(alreadyChecked)return;
        selectedOption=option;
        answers.querySelectorAll("button").forEach(b=>b.classList.remove("selected"));
        button.classList.add("selected");
      });
      answers.append(button);
    });
    box.append(answers);
  }else{
    const input=document.createElement("input");
    input.type="text";
    input.id="answerInput";
    input.className="answer-input";
    input.autocomplete="off";
    input.placeholder="Deine Antwort eingeben …";
    input.setAttribute("aria-label","Deine Antwort");
    input.addEventListener("keydown",e=>{
      if(e.key==="Enter"&&!alreadyChecked)checkAnswer();
    });
    box.append(input);
    setTimeout(()=>input.focus(),50);
  }
  area.append(number,box);
}

function normalizeAnswer(value){
  return String(value).trim().toLowerCase().replaceAll(",",".").replace(/\s+/g," ");
}

function checkAnswer(){
  if(alreadyChecked)return;
  const q=questions[questionIndex];
  let answer;

  if(q.options.length){
    answer=selectedOption;
    if(answer===null){
      showFeedback("Bitte wähle zuerst eine Antwort aus.",false,true);
      return;
    }
  }else{
    const input=document.getElementById("answerInput");
    answer=input?input.value.trim():"";
    if(!answer){
      showFeedback("Bitte gib zuerst eine Antwort ein.",false,true);
      return;
    }
  }

  alreadyChecked=true;
  const correct=normalizeAnswer(answer)===normalizeAnswer(q.a);
  progress.attempts++;
  if(correct){currentCorrect++;progress.correct++;}
  saveProgress();
  updateStats();

  document.querySelectorAll(".answer-option").forEach(button=>{
    button.disabled=true;
    if(normalizeAnswer(button.textContent)===normalizeAnswer(q.a)){
      button.style.borderColor="#16a34a";
      button.style.background="#dcfce7";
    }else if(normalizeAnswer(button.textContent)===normalizeAnswer(answer)&&!correct){
      button.style.borderColor="#dc2626";
      button.style.background="#fee2e2";
    }
  });

  const input=document.getElementById("answerInput");
  if(input)input.disabled=true;

  let message=correct?"✅ Richtig! Sehr gut!":`❌ Leider falsch. Die richtige Antwort lautet: ${q.a}.`;
  if(q.explanation)message+="\n"+q.explanation;

  showFeedback(message,correct);
  document.getElementById("checkAnswer").classList.add("hidden");
  document.getElementById("nextQuestion").classList.remove("hidden");
  document.getElementById("quizProgress").style.width=`${(questionIndex+1)/questions.length*100}%`;
}

function showFeedback(message,correct,neutral=false){
  const feedback=document.getElementById("questionFeedback");
  feedback.className="feedback"+(neutral?"":correct?" correct":" wrong");
  feedback.textContent=message;
}

document.getElementById("checkAnswer").addEventListener("click",checkAnswer);

document.getElementById("nextQuestion").addEventListener("click",()=>{
  if(!alreadyChecked)return;
  questionIndex++;
  if(questionIndex>=questions.length)finishQuiz();
  else renderQuestion();
});

function finishQuiz(){
  const total=questions.length;
  const percent=Math.round(currentCorrect/total*100);
  const points=currentCorrect*10;

  markCompleted(activeLesson.id,points);

  document.getElementById("resultTitle").textContent=
    percent>=80?"🎉 Super gemacht!":percent>=50?"👏 Gut geübt!":"💪 Weiter üben!";
  document.getElementById("resultText").textContent=
    `Du hast ${points} Punkte gesammelt. Du kannst die Lektion wiederholen oder verlassen.`;
  document.getElementById("resultCorrect").textContent=currentCorrect;
  document.getElementById("resultTotal").textContent=total;
  document.getElementById("resultPercent").textContent=percent+"%";

  renderLessons();
  showPage("result");
}

document.getElementById("retryQuiz").addEventListener("click",startQuiz);

/* ============================
   SCHACHBEREICH
============================ */

let chess=null;
let chessStarted=false;
let selectedSquare=null;
let legalTargets=[];
let flipped=false;
let botThinking=false;
let lastMove=null;
let botSearchToken=0;

const glyphs={
  wk:"♔",wq:"♕",wr:"♖",wb:"♗",wn:"♘",wp:"♙",
  bk:"♚",bq:"♛",br:"♜",bb:"♝",bn:"♞",bp:"♟"
};

const pieceValue={p:100,n:320,b:335,r:500,q:900,k:20000};

function squareName(file,rank){return "abcdefgh"[file]+rank;}

function startChess(){
  if(typeof Chess==="undefined"){
    alert("Die Schachbibliothek konnte nicht geladen werden. Prüfe bitte deine Internetverbindung.");
    return;
  }

  botSearchToken++;
  chess=new Chess();
  chessStarted=true;
  selectedSquare=null;
  legalTargets=[];
  lastMove=null;
  botThinking=false;
  flipped=document.getElementById("humanColor").value==="b";

  document.getElementById("chessSetup").classList.add("hidden");
  document.getElementById("chessGame").classList.remove("hidden");
  document.getElementById("activeBotLabel").textContent=
    "BOT · STUFE "+document.getElementById("botLevel").value;

  renderBoard();

  if(document.getElementById("humanColor").value==="b"){
    setTimeout(makeBotMove,200);
  }
}

document.getElementById("startChessGame").addEventListener("click",startChess);

document.getElementById("botLevel").addEventListener("change",()=>{
  const level=Number(document.getElementById("botLevel").value);
  const descriptions={
    1:"Zufällige und einfache Züge. Gut für absolute Anfänger.",
    2:"Sucht kurze Antworten und nimmt gelegentlich kostenlose Figuren.",
    3:"Berücksichtigt Material, Zentrum und einfache Taktik.",
    4:"Sucht mehrere Halbzüge voraus und vermeidet mehr direkte Fehler.",
    5:"Nutzt tiefere Suche, Zugreihenfolge und genauere Positionsbewertung.",
    6:"Die stärkste Stufe dieses Bots. Rechnet weiter voraus und bewertet taktische Möglichkeiten sorgfältiger."
  };
  document.getElementById("botDescription").innerHTML=
    `<strong>Stufe ${level}</strong><br>${descriptions[level]}`;
});

function renderBoard(){
  if(!chess||!chessStarted)return;
  const boardElement=document.getElementById("chessBoard");
  boardElement.innerHTML="";
  const position=chess.board();

  const files=flipped?[7,6,5,4,3,2,1,0]:[0,1,2,3,4,5,6,7];
  const ranks=flipped?[0,1,2,3,4,5,6,7]:[7,6,5,4,3,2,1,0];

  for(const rankIndex of ranks){
    for(const fileIndex of files){
      const rank=7-rankIndex;
      const file=fileIndex;
      const name=squareName(file,rank+1);
      const piece=position[rankIndex][fileIndex];

      const square=document.createElement("button");
      square.type="button";
      square.className="square "+((rankIndex+fileIndex)%2?"dark":"light");
      square.setAttribute("aria-label",name+(piece?" "+piece.type:""));

      if(selectedSquare===name)square.classList.add("selected");
      if(lastMove&&(lastMove.from===name||lastMove.to===name))square.classList.add("lastmove");
      if(legalTargets.includes(name)){
        square.classList.add("legal");
        if(piece)square.classList.add("capture");
      }

      if(piece){
        const color=piece.color;
        square.innerHTML=`<span class="piece-${color==="w"?"white":"black"}">${glyphs[color+piece.type]}</span>`;
      }

      square.addEventListener("click",()=>onSquareClick(name));
      boardElement.append(square);
    }
  }

  renderMoveList();
  updateChessStatus();
}

function onSquareClick(square){
  if(!chess||chess.isGameOver()||botThinking)return;
  if(chess.turn()!==document.getElementById("humanColor").value)return;

  const piece=chess.get(square);

  if(selectedSquare&&legalTargets.includes(square)){
    const candidateMoves=chess.moves({square:selectedSquare,verbose:true});
    const needsPromotion=candidateMoves.some(m=>m.to===square&&m.promotion);
    let move={from:selectedSquare,to:square};

    if(needsPromotion){
      const choice=prompt("Bauernumwandlung: Dame, Turm, Läufer oder Springer? (d/t/l/s)","d");
      const map={d:"q",t:"r",l:"b",s:"n"};
      move.promotion=map[(choice||"d").toLowerCase()]||"q";
    }

    playHumanMove(move);
    return;
  }

  if(piece&&piece.color===chess.turn()){
    selectedSquare=square;
    legalTargets=chess.moves({square,verbose:true}).map(m=>m.to);
  }else{
    selectedSquare=null;
    legalTargets=[];
  }
  renderBoard();
}

function playHumanMove(move){
  try{
    const result=chess.move(move);
    if(!result)return;

    lastMove={from:result.from,to:result.to};
    selectedSquare=null;
    legalTargets=[];
    renderBoard();

    if(!chess.isGameOver()&&chess.turn()!==document.getElementById("humanColor").value){
      setTimeout(makeBotMove,120);
    }
  }catch(error){
    selectedSquare=null;
    legalTargets=[];
    renderBoard();
  }
}

function updateChessStatus(){
  if(!chess)return;
  const status=document.getElementById("chessStatus");

  if(chess.isCheckmate()){
    status.textContent="♚ Schachmatt! "+(chess.turn()==="w"?"Schwarz":"Weiß")+" gewinnt.";
  }else if(chess.isStalemate()){
    status.textContent="🤝 Patt – die Partie endet remis.";
  }else if(chess.isDraw()){
    status.textContent="🤝 Remis – die Partie ist unentschieden.";
  }else if(botThinking){
    status.textContent="🤖 Der Bot überlegt …";
  }else if(chess.isCheck()){
    status.textContent="⚠️ Schach! "+(chess.turn()==="w"?"Weiß":"Schwarz")+" muss reagieren.";
  }else{
    status.textContent=(chess.turn()==="w"?"Weiß":"Schwarz")+" ist am Zug.";
  }
}

function renderMoveList(){
  if(!chess)return;
  const moves=chess.history();
  const list=document.getElementById("moveList");

  if(!moves.length){list.textContent="Noch keine Züge.";return;}

  let text="";
  for(let i=0;i<moves.length;i+=2){
    text+=`${Math.floor(i/2)+1}. ${moves[i]} `;
    if(moves[i+1])text+=`${moves[i+1]} `;
    text+="\n";
  }
  list.textContent=text;
  list.scrollTop=list.scrollHeight;
}

/* Bot: Material, Zentrum, Bauernstruktur und Königssicherheit bewerten. */
function evaluatePosition(game){
  if(game.isCheckmate())return game.turn()==="w"?-100000:100000;
  if(game.isDraw())return 0;

  let score=0;
  const board=game.board();

  for(let r=0;r<8;r++){
    for(let f=0;f<8;f++){
      const p=board[r][f];
      if(!p)continue;

      const sign=p.color==="w"?1:-1;
      const value=pieceValue[p.type];
      score+=sign*value;

      const center=3.5-(Math.abs(3.5-f)+Math.abs(3.5-r))/2;
      if(p.type==="n")score+=sign*center*11;
      if(p.type==="b")score+=sign*center*7;
      if(p.type==="q")score+=sign*center*3;

      if(p.type==="p"){
        const advancement=p.color==="w"?6-r:r-1;
        score+=sign*advancement*8;
        if(f===3||f===4)score+=sign*8;
      }

      if(p.type==="k"){
        const home=p.color==="w"?7:0;
        if(r===home)score+=sign*12;
      }
    }
  }

  // Mobilität: mehr legale Möglichkeiten sind oft vorteilhaft.
  const turn=game.turn();
  const mobility=game.moves().length;
  score+=(turn==="w"?1:-1)*mobility*2;

  return score;
}

function moveOrdering(moves){
  return moves.sort((a,b)=>{
    const score=m=>{
      let s=0;
      if(m.captured)s+=10*pieceValue[m.captured]-pieceValue[m.piece];
      if(m.promotion)s+=pieceValue[m.promotion]-100;
      if(m.san&&m.san.includes("#"))s+=100000;
      else if(m.san&&m.san.includes("+"))s+=40;
      return s;
    };
    return score(b)-score(a);
  });
}

function minimax(game,depth,alpha,beta,maximizing){
  if(depth===0||game.isGameOver())return evaluatePosition(game);

  const moves=moveOrdering(game.moves({verbose:true}));
  if(!moves.length)return evaluatePosition(game);

  if(maximizing){
    let best=-Infinity;
    for(const move of moves){
      game.move(move);
      const score=minimax(game,depth-1,alpha,beta,false);
      game.undo();
      if(score>best)best=score;
      if(best>alpha)alpha=best;
      if(beta<=alpha)break;
    }
    return best;
  }else{
    let best=Infinity;
    for(const move of moves){
      game.move(move);
      const score=minimax(game,depth-1,alpha,beta,true);
      game.undo();
      if(score<best)best=score;
      if(best<beta)beta=best;
      if(beta<=alpha)break;
    }
    return best;
  }
}

function chooseBotMove(){
  const level=Number(document.getElementById("botLevel").value);
  const moves=chess.moves({verbose:true});
  if(!moves.length)return null;

  const depthByLevel={1:1,2:1,3:2,4:3,5:3,6:4};
  const depth=depthByLevel[level]||2;
  const botColor=chess.turn();

  // Die beiden niedrigsten Stufen spielen absichtlich weniger präzise.
  if(level===1){
    return moves[Math.floor(Math.random()*moves.length)];
  }

  let bestMoves=[];
  let bestScore=botColor==="w"?-Infinity:Infinity;

  const candidates=moveOrdering(moves);

  for(const move of candidates){
    chess.move(move);
    let score=minimax(chess,depth-1,-Infinity,Infinity,botColor==="b");
    chess.undo();

    // Kleine Ungenauigkeiten auf mittleren Stufen sorgen für Unterschiede.
    if(level===2)score+=(Math.random()-.5)*180;
    if(level===3)score+=(Math.random()-.5)*45;
    if(level===4)score+=(Math.random()-.5)*12;

    if(botColor==="w"){
      if(score>bestScore){bestScore=score;bestMoves=[move];}
      else if(score===bestScore)bestMoves.push(move);
    }else{
      if(score<bestScore){bestScore=score;bestMoves=[move];}
      else if(score===bestScore)bestMoves.push(move);
    }
  }

  return bestMoves[Math.floor(Math.random()*bestMoves.length)]||moves[0];
}

function makeBotMove(){
  if(!chess||!chessStarted||chess.isGameOver())return;
  if(chess.turn()===document.getElementById("humanColor").value)return;

  botThinking=true;
  renderBoard();
  const token=++botSearchToken;

  // Kurze Verzögerung, damit der Status „Bot überlegt“ sichtbar ist.
  setTimeout(()=>{
    if(token!==botSearchToken||!chess||chess.isGameOver()){
      botThinking=false;
      renderBoard();
      return;
    }

    const move=chooseBotMove();
    if(move){
      const result=chess.move(move);
      if(result)lastMove={from:result.from,to:result.to};
    }

    botThinking=false;
    selectedSquare=null;
    legalTargets=[];
    renderBoard();
  },80);
}

function restartCurrentGame(){
  if(!chessStarted)return;
  startChess();
}

document.getElementById("restartChess").addEventListener("click",restartCurrentGame);

document.getElementById("undoChess").addEventListener("click",()=>{
  if(!chess||botThinking)return;

  botSearchToken++;
  chess.undo();

  // Wenn der Bot bereits geantwortet hat, werden beide Züge zurückgenommen.
  if(chess.history().length&&chess.turn()!==document.getElementById("humanColor").value){
    chess.undo();
  }

  selectedSquare=null;
  legalTargets=[];
  botThinking=false;
  lastMove=null;

  const history=chess.history({verbose:true});
  if(history.length){
    const last=history[history.length-1];
    lastMove={from:last.from,to:last.to};
  }

  renderBoard();
});

document.getElementById("flipBoard").addEventListener("click",()=>{
  flipped=!flipped;
  renderBoard();
});

document.getElementById("exitChess").addEventListener("click",()=>{
  botSearchToken++;
  botThinking=false;
  chessStarted=false;
  chess=null;
  selectedSquare=null;
  legalTargets=[];
  lastMove=null;
  document.getElementById("chessGame").classList.add("hidden");
  document.getElementById("chessSetup").classList.remove("hidden");
  showPage("learn");
});

/* Initialisieren */
updateStats();
renderLessons();
</script>
</body>
</html>

Game Source: LernCampus – Lernen & Schach

Creator: RocketWave60

Libraries: none

Complexity: complex (1174 lines, 41.7 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: lerncampus-interaktiv-lernen-klasse-1-6-rocketwave60" to link back to the original. Then publish at arcadelab.ai/publish.