LernCampus – Lernen & Schach
by RocketWave601174 lines41.7 KB
<!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.