Initial commit - Kinderspiele

This commit is contained in:
OpenClaw
2026-04-26 09:44:19 +02:00
commit 35817527c8
29 changed files with 10949 additions and 0 deletions
+273
View File
@@ -0,0 +1,273 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Autorennen - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #1a472a 0%, #0f2918 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 15px;
}
h1 { color: white; margin-bottom: 10px; font-size: 24px; }
.track-container {
background: #2d3748; border-radius: 15px; padding: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.5); margin: 15px 0;
}
.track {
width: 320px; height: 60px; background: linear-gradient(90deg, #4a5568 0%, #718096 50%, #4a5568 100%);
border-radius: 10px; margin: 10px 0; position: relative;
border: 3px solid #e2e8f0; overflow: hidden;
}
.track::before {
content: ''; position: absolute; top: 50%; left: 0; right: 0;
height: 4px; background: repeating-linear-gradient(90deg, #fff 0px, #fff 20px, transparent 20px, transparent 40px);
transform: translateY(-50%);
}
.finish-line {
position: absolute; right: 10px; top: 0; bottom: 0; width: 8px;
background: repeating-linear-gradient(180deg, #fff 0px, #fff 10px, #000 10px, #000 20px);
}
.car {
position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
font-size: 40px; transition: left 0.5s ease-out;
filter: drop-shadow(2px 2px 4px rgba(0,0,0,0.5));
}
.car.moving { animation: bounce 0.3s ease-in-out; }
@keyframes bounce {
0%, 100% { transform: translateY(-50%) scale(1); }
50% { transform: translateY(-60%) scale(1.1); }
}
.dice-container {
perspective: 1000px; margin: 20px 0;
}
.dice {
width: 80px; height: 80px; position: relative;
transform-style: preserve-3d; cursor: pointer;
}
.dice.rolling { animation: rollDice 0.8s ease-out; }
@keyframes rollDice {
0% { transform: rotateX(0) rotateY(0) rotateZ(0); }
100% { transform: rotateX(720deg) rotateY(720deg) rotateZ(360deg); }
}
.face {
position: absolute; width: 80px; height: 80px;
background: white; border: 2px solid #333; border-radius: 12px;
display: flex; justify-content: center; align-items: center;
font-size: 40px; font-weight: bold;
}
.face-1 { transform: rotateY(0deg) translateZ(40px); }
.face-2 { transform: rotateY(90deg) translateZ(40px); }
.face-3 { transform: rotateY(180deg) translateZ(40px); }
.face-4 { transform: rotateY(-90deg) translateZ(40px); }
.face-5 { transform: rotateX(90deg) translateZ(40px); }
.face-6 { transform: rotateX(-90deg) translateZ(40px); }
.dot-grid { display: grid; gap: 5px; }
.dot-grid.col-2 { grid-template-columns: 1fr 1fr; }
.dot { width: 12px; height: 12px; background: #333; border-radius: 50%; }
.status { color: white; font-size: 20px; margin: 15px 0; text-align: center; }
.turn-indicator {
background: rgba(255,255,255,0.2); padding: 10px 20px; border-radius: 20px;
color: white; margin: 10px 0; font-size: 18px;
}
.turn-indicator .player { font-weight: bold; }
.turn-indicator .player.red { color: #ff6b6b; }
.turn-indicator .player.blue { color: #4dabf7; }
.controls { display: flex; gap: 15px; margin-top: 20px; flex-wrap: wrap; justify-content: center; }
.btn {
background: white; border: none; border-radius: 10px; padding: 12px 25px;
font-size: 16px; cursor: pointer; font-family: inherit; font-weight: bold;
}
.btn-roll { background: #4ade80; color: white; font-size: 18px; padding: 15px 40px; }
.btn-roll:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-back { background: #ff6b6b; color: white; text-decoration: none; }
.winner {
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: white; padding: 30px 50px; border-radius: 20px;
text-align: center; display: none; z-index: 100;
box-shadow: 0 10px 50px rgba(0,0,0,0.5);
}
.winner h2 { color: #333; margin-bottom: 15px; }
.winner-emoji { font-size: 60px; }
.instructions { color: #aaa; text-align: center; margin: 10px 0; font-size: 14px; }
</style>
</head>
<body>
<h1>🏎️ Autorennen</h1>
<p class="instructions">Würfle und bewege dein Auto! Wer zuerst im Ziel ist, gewinnt!</p>
<div class="turn-indicator" id="turnIndicator">
<span class="player red">🔴 Rot</span> ist dran!
</div>
<div class="track-container">
<div class="track">
<div class="finish-line"></div>
<div class="car" id="car1">🔴</div>
</div>
<div class="track">
<div class="finish-line"></div>
<div class="car" id="car2">🔵</div>
</div>
</div>
<div class="status" id="status">Klicke auf den Würfel!</div>
<div class="dice-container">
<div class="dice" id="dice" onclick="roll()">
<div class="face face-1"><div class="dot-grid"><div class="dot"></div></div></div>
<div class="face face-2"><div class="dot-grid col-2"><div class="dot"></div><div class="dot"></div></div></div>
<div class="face face-3"><div class="dot-grid col-2"><div class="dot"></div><div class="dot"></div><div class="dot"></div></div></div>
<div class="face face-4"><div class="dot-grid col-2"><div class="dot"></div><div class="dot"></div><div class="dot"></div><div class="dot"></div></div></div>
<div class="face face-5"><div class="dot-grid col-2"><div class="dot"></div><div class="dot"></div><div class="dot"></div><div class="dot"></div><div class="dot"></div></div></div>
<div class="face face-6"><div class="dot-grid col-2"><div class="dot"></div><div class="dot"></div><div class="dot"></div><div class="dot"></div><div class="dot"></div><div class="dot"></div></div></div>
</div>
</div>
<div class="controls">
<button class="btn btn-roll" id="rollBtn" onclick="roll()">🎲 Würfeln</button>
<button class="btn" onclick="resetGame()">🔄 Neustart</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<div class="winner" id="winner">
<h2>🎉 Gewinner! 🎉</h2>
<div class="winner-emoji" id="winnerEmoji"></div>
<button class="btn btn-roll" onclick="resetGame()" style="margin-top: 20px">🔄 Nochmal</button>
</div>
<script>
const TRACK_LENGTH = 280; // Pixel bis zum Ziel
const STEPS_TO_FINISH = 20; // 20 Felder bis zum Ziel
let positions = [0, 0]; // Position der Autos (in Schritten)
let currentPlayer = 0; // 0 = Rot, 1 = Blau
let rolling = false;
let gameEnded = false;
const cars = [document.getElementById('car1'), document.getElementById('car2')];
const dice = document.getElementById('dice');
const status = document.getElementById('status');
const turnIndicator = document.getElementById('turnIndicator');
const rotations = {
1: 'rotateX(0deg) rotateY(0deg)',
2: 'rotateX(0deg) rotateY(-90deg)',
3: 'rotateX(0deg) rotateY(180deg)',
4: 'rotateX(0deg) rotateY(90deg)',
5: 'rotateX(-90deg) rotateY(0deg)',
6: 'rotateX(90deg) rotateY(0deg)'
};
function roll() {
if (rolling || gameEnded) return;
rolling = true;
// Animation
dice.classList.add('rolling');
// Zufällige Zahl
const result = Math.floor(Math.random() * 6) + 1;
setTimeout(() => {
dice.classList.remove('rolling');
dice.style.transform = rotations[result];
// Auto bewegen
moveCar(currentPlayer, result);
setTimeout(() => {
rolling = false;
}, 500);
}, 800);
}
function moveCar(player, steps) {
const car = cars[player];
car.classList.add('moving');
positions[player] += steps;
// Position berechnen (in Pixel)
const pixelPos = Math.min(positions[player] / STEPS_TO_FINISH * TRACK_LENGTH, TRACK_LENGTH);
car.style.left = (10 + pixelPos) + 'px';
// Status aktualisieren
const playerName = player === 0 ? '🔴 Rot' : '🔵 Blau';
status.textContent = playerName + ' würfelt ' + steps + '!';
// TTS
if ('speechSynthesis' in window) {
const utterance = new SpeechSynthesisUtterance(playerName.replace('🔴', 'Rot').replace('🔵', 'Blau') + ' würfelt ' + steps);
utterance.lang = 'de-DE';
speechSynthesis.speak(utterance);
}
setTimeout(() => {
car.classList.remove('moving');
// Prüfen ob gewonnen
if (positions[player] >= STEPS_TO_FINISH) {
endGame(player);
} else {
// Nächster Spieler
switchPlayer();
}
}, 500);
}
function switchPlayer() {
currentPlayer = currentPlayer === 0 ? 1 : 0;
if (currentPlayer === 0) {
turnIndicator.innerHTML = '<span class="player red">🔴 Rot</span> ist dran!';
} else {
turnIndicator.innerHTML = '<span class="player blue">🔵 Blau</span> ist dran!';
}
}
function endGame(winner) {
gameEnded = true;
const winnerEmoji = winner === 0 ? '🔴' : '🔵';
const winnerName = winner === 0 ? 'Rot' : 'Blau';
document.getElementById('winnerEmoji').textContent = winnerEmoji + ' ' + winnerName + ' gewinnt!';
document.getElementById('winner').style.display = 'block';
if ('speechSynthesis' in window) {
const utterance = new SpeechSynthesisUtterance(winnerName + ' gewinnt das Rennen!');
utterance.lang = 'de-DE';
speechSynthesis.speak(utterance);
}
}
function resetGame() {
positions = [0, 0];
currentPlayer = 0;
gameEnded = false;
rolling = false;
cars.forEach(car => {
car.style.left = '10px';
car.classList.remove('moving');
});
dice.style.transform = rotations[1];
status.textContent = 'Klicke auf den Würfel!';
turnIndicator.innerHTML = '<span class="player red">🔴 Rot</span> ist dran!';
document.getElementById('winner').style.display = 'none';
}
</script>
</body>
</html>
+246
View File
@@ -0,0 +1,246 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Buchstaben nachmalen - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 10px;
}
h1 { color: white; margin-bottom: 10px; font-size: 22px; }
.instruction { color: white; font-size: 18px; margin-bottom: 10px; text-align: center; }
.main-area {
display: flex; flex-direction: column; align-items: center; gap: 15px;
}
.letter-section {
display: flex; flex-direction: column; align-items: center; gap: 10px;
}
.target-letter {
font-size: 80px; background: white; border-radius: 15px;
padding: 10px 30px; box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.arrow { font-size: 30px; color: white; }
.drawing-area { position: relative; }
#drawCanvas {
background: white; border: 3px solid #4ade80; border-radius: 15px;
box-shadow: 0 5px 15px rgba(0,0,0,0.2); touch-action: none;
}
.template-overlay {
position: absolute; top: 0; left: 0; pointer-events: none;
font-size: 200px; color: rgba(200,200,200,0.3); text-align: center;
line-height: 300px; width: 300px; height: 300px;
}
.letter-grid {
display: grid; grid-template-columns: repeat(10, 1fr);
gap: 5px; max-width: 500px; width: 100%; margin: 10px 0;
}
.letter-btn {
background: rgba(255,255,255,0.9); border: none; border-radius: 8px;
padding: 10px 5px; font-size: 20px; cursor: pointer; font-family: inherit;
transition: all 0.2s;
}
.letter-btn:hover { background: white; transform: scale(1.1); }
.letter-btn.active { background: #4ade80; color: white; }
.controls { display: flex; gap: 15px; margin-top: 10px; flex-wrap: wrap; justify-content: center; }
.btn {
background: white; border: none; border-radius: 10px; padding: 12px 25px;
font-size: 16px; cursor: pointer; font-family: inherit; font-weight: bold;
}
.btn-check { background: #4ade80; color: white; }
.btn-clear { background: #feca57; }
.btn-back { background: #ff6b6b; color: white; text-decoration: none; }
.feedback {
font-size: 24px; margin: 10px 0; min-height: 40px; font-weight: bold;
color: white; text-align: center;
}
.score { color: white; font-size: 18px; margin-top: 5px; }
</style>
</head>
<body>
<h1>🔤 Buchstaben nachmalen</h1>
<div class="instruction" id="instruction">Male den Buchstaben nach!</div>
<div class="main-area">
<div class="letter-section">
<div class="target-letter" id="targetLetter">A</div>
<div class="arrow">⬇️</div>
<div class="drawing-area">
<canvas id="drawCanvas" width="300" height="300"></canvas>
<div class="template-overlay" id="templateOverlay">A</div>
</div>
</div>
</div>
<div class="feedback" id="feedback"></div>
<div class="score">Richtig: <span id="correctCount">0</span></div>
<div class="controls">
<button class="btn btn-check" onclick="checkDrawing()">✓ Fertig!</button>
<button class="btn btn-clear" onclick="clearCanvas()">🗑️ Löschen</button>
<button class="btn" onclick="nextLetter()">🔄 Nächster</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<div class="letter-grid" id="letterGrid"></div>
<script>
const letters = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','Ä','Ö','Ü'];
const synth = window.speechSynthesis;
let currentLetter = 'A';
let currentIndex = 0;
let isDrawing = false;
let correctCount = 0;
let hasDrawn = false;
const canvas = document.getElementById('drawCanvas');
const ctx = canvas.getContext('2d');
const templateOverlay = document.getElementById('templateOverlay');
// Setup canvas
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.lineWidth = 8;
ctx.strokeStyle = '#333';
function initGrid() {
const grid = document.getElementById('letterGrid');
letters.forEach((letter, index) => {
const btn = document.createElement('button');
btn.className = 'letter-btn';
btn.textContent = letter;
btn.onclick = () => selectLetter(index);
grid.appendChild(btn);
});
}
function selectLetter(index) {
currentIndex = index;
currentLetter = letters[index];
updateDisplay();
}
function updateDisplay() {
document.getElementById('targetLetter').textContent = currentLetter;
templateOverlay.textContent = currentLetter;
document.getElementById('instruction').textContent = `Male den Buchstaben "${currentLetter}" nach!`;
// Highlight
document.querySelectorAll('.letter-btn').forEach((btn, i) => {
btn.classList.toggle('active', i === currentIndex);
});
clearCanvas();
speak(`Male den Buchstaben ${currentLetter}`);
}
function nextLetter() {
currentIndex = (currentIndex + 1) % letters.length;
selectLetter(currentIndex);
}
// Drawing functions
function getPos(e) {
const rect = canvas.getBoundingClientRect();
const clientX = e.touches ? e.touches[0].clientX : e.clientX;
const clientY = e.touches ? e.touches[0].clientY : e.clientY;
return {
x: clientX - rect.left,
y: clientY - rect.top
};
}
function startDraw(e) {
isDrawing = true;
hasDrawn = true;
const pos = getPos(e);
ctx.beginPath();
ctx.moveTo(pos.x, pos.y);
e.preventDefault();
}
function draw(e) {
if (!isDrawing) return;
const pos = getPos(e);
ctx.lineTo(pos.x, pos.y);
ctx.stroke();
e.preventDefault();
}
function stopDraw() {
isDrawing = false;
}
function clearCanvas() {
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
hasDrawn = false;
document.getElementById('feedback').textContent = '';
}
function checkDrawing() {
if (!hasDrawn) {
document.getElementById('feedback').textContent = '⚠️ Male erst etwas!';
return;
}
// Einfache Prüfung: Hat der Benutzer genug gezeichnet?
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const pixels = imageData.data;
let drawnPixels = 0;
// Zähle nicht-weiße Pixel
for (let i = 3; i < pixels.length; i += 4) {
if (pixels[i] > 0) drawnPixels++;
}
// Mindestens 500 Pixel müssen gezeichnet sein
const threshold = 500;
if (drawnPixels > threshold) {
correctCount++;
document.getElementById('correctCount').textContent = correctCount;
document.getElementById('feedback').textContent = '🌟 Sehr gut! Super gemalt!';
document.getElementById('feedback').style.color = '#4ade80';
speak(`Sehr gut! Du hast den Buchstaben ${currentLetter} toll gemalt!`);
// Automatisch nächster nach 2 Sekunden
setTimeout(nextLetter, 2000);
} else {
document.getElementById('feedback').textContent = '✏️ Versuch es nochmal! Male den Buchstaben größer.';
document.getElementById('feedback').style.color = '#feca57';
speak('Versuch es nochmal. Male den Buchstaben größer und deutlicher.');
}
}
function speak(text) {
if (!synth) return;
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'de-DE';
utterance.rate = 0.8;
utterance.pitch = 1.2;
synth.speak(utterance);
}
// Event listeners
canvas.addEventListener('mousedown', startDraw);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', stopDraw);
canvas.addEventListener('mouseout', stopDraw);
canvas.addEventListener('touchstart', startDraw);
canvas.addEventListener('touchmove', draw);
canvas.addEventListener('touchend', stopDraw);
// Init
initGrid();
clearCanvas();
updateDisplay();
</script>
</body>
</html>
+424
View File
@@ -0,0 +1,424 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Halli Galli - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 15px;
}
h1 { color: white; margin-bottom: 5px; font-size: 24px; }
.age-hint {
color: #FEF3C7; font-size: 14px; margin-bottom: 10px; text-align: center;
}
.difficulty-select {
display: flex; gap: 8px; margin: 10px 0; flex-wrap: wrap; justify-content: center;
}
.diff-btn {
background: rgba(255,255,255,0.2); border: 2px solid white; color: white;
padding: 8px 15px; border-radius: 20px; cursor: pointer; font-family: inherit;
font-size: 13px; transition: all 0.2s;
}
.diff-btn.active {
background: white; color: #667eea; font-weight: bold;
transform: scale(1.05);
}
.diff-btn:hover { background: rgba(255,255,255,0.4); }
.diff-desc {
color: #FEF3C7; font-size: 12px; text-align: center; margin: 5px 0; max-width: 350px;
min-height: 30px;
}
.game-area {
display: flex; gap: 20px; margin: 15px 0; flex-wrap: wrap; justify-content: center;
}
.card-display {
width: 130px; height: 180px; background: white; border-radius: 15px;
display: flex; flex-direction: column; align-items: center; justify-content: center;
box-shadow: 0 10px 30px rgba(0,0,0,0.3); position: relative; border: 4px solid #333;
transition: all 0.3s;
}
.card-display.match {
animation: pulse-match 0.5s ease-in-out;
border-color: #FFD700;
box-shadow: 0 0 30px #FFD700;
}
@keyframes pulse-match {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
.card-emoji { font-size: 60px; }
.card-number {
font-size: 36px; font-weight: bold; color: #333;
margin-top: 5px;
}
.card-count {
position: absolute; bottom: 10px; font-size: 12px; color: #666; font-weight: bold;
}
.player-name {
position: absolute; top: 10px; font-size: 11px; color: #999; font-weight: bold;
}
.big-hint {
position: absolute; top: -40px; left: 50%; transform: translateX(-50%);
background: #FFD700; color: #333; padding: 5px 15px; border-radius: 20px;
font-size: 14px; font-weight: bold; display: none; animation: bounce 0.5s infinite;
}
@keyframes bounce {
0%, 100% { transform: translateX(-50%) translateY(0); }
50% { transform: translateX(-50%) translateY(-5px); }
}
.bell-area {
width: 160px; height: 160px; background: linear-gradient(135deg, #FFD700, #FFA500);
border-radius: 50%; display: flex; flex-direction: column; align-items: center; justify-content: center;
cursor: pointer; box-shadow: 0 10px 30px rgba(0,0,0,0.4);
border: 8px solid #FF8C00; transition: all 0.1s; position: relative;
}
.bell-area:active { transform: scale(0.95); }
.bell { font-size: 80px; }
.bell.ringing { animation: ring 0.3s ease-in-out; }
@keyframes ring {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(-15deg); }
75% { transform: rotate(15deg); }
}
.bell-hint {
position: absolute; bottom: 15px; font-size: 11px; color: #333; font-weight: bold;
}
.score-board {
display: flex; gap: 30px; margin: 10px 0; color: white; font-size: 16px;
}
.score-item { text-align: center; }
.score-value { font-size: 28px; font-weight: bold; color: #feca57; }
.status {
color: white; font-size: 18px; margin: 10px 0; text-align: center;
min-height: 50px; font-weight: bold;
}
.fruit-counter {
display: flex; gap: 10px; margin: 10px 0; flex-wrap: wrap; justify-content: center;
}
.fruit-box {
background: rgba(255,255,255,0.2); padding: 8px 12px; border-radius: 10px;
color: white; font-size: 13px; text-align: center; min-width: 70px;
}
.fruit-box.highlight {
background: #FFD700; color: #333; transform: scale(1.1);
box-shadow: 0 0 15px rgba(255,215,0,0.5);
}
.fruit-count { font-size: 20px; font-weight: bold; }
.controls { display: flex; gap: 15px; margin-top: 15px; }
.btn {
background: white; border: none; border-radius: 10px; padding: 12px 25px;
font-size: 16px; cursor: pointer; font-family: inherit; font-weight: bold;
}
.btn-start { background: #4ade80; color: white; }
.btn-back { background: #ff6b6b; color: white; text-decoration: none; }
.game-over {
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: white; padding: 30px; border-radius: 20px; text-align: center;
display: none; z-index: 100; box-shadow: 0 10px 50px rgba(0,0,0,0.5);
}
.game-over h2 { margin-bottom: 15px; }
</style>
</head>
<body>
<h1>🔔 Halli Galli</h1>
<div class="age-hint">👶 Kindermodus: Große Zahlen, visuelle Hilfen, mehr Zeit</div>
<div class="difficulty-select" id="diffSelect">
<button class="diff-btn active" onclick="setDifficulty('baby')">👶 Baby (5 J.)</button>
<button class="diff-btn" onclick="setDifficulty('easy')">🟢 Einfach</button>
<button class="diff-btn" onclick="setDifficulty('medium')">🟡 Mittel</button>
<button class="diff-btn" onclick="setDifficulty('hard')">🔴 Schwer</button>
</div>
<div class="diff-desc" id="diffDesc">Visuelle Hilfen + 4 Sekunden Zeit zum Reagieren</div>
<div class="score-board">
<div class="score-item">
<div>Deine Karten</div>
<div class="score-value" id="playerCards">20</div>
</div>
<div class="score-item">
<div>Computer</div>
<div class="score-value" id="botCards">20</div>
</div>
</div>
<div class="game-area">
<div class="card-display" id="playerCard">
<div class="big-hint" id="playerHint">🔔 KLINGELN!</div>
<div class="player-name">DU</div>
<div class="card-emoji" id="playerEmoji"></div>
<div class="card-number" id="playerNum"></div>
<div class="card-count" id="playerCount">20 Karten</div>
</div>
<div class="bell-area" id="bell" onclick="ringBell()">
<div class="bell">🔔</div>
<div class="bell-hint" id="bellHint">Klick mich!</div>
</div>
<div class="card-display" id="botCard">
<div class="player-name">COMPUTER</div>
<div class="card-emoji" id="botEmoji"></div>
<div class="card-number" id="botNum"></div>
<div class="card-count" id="botCount">20 Karten</div>
</div>
</div>
<div class="status" id="status">Wähle den Schwierigkeitsgrad für deine Tochter! 👧</div>
<div class="fruit-counter" id="fruitCounter">
<div class="fruit-box" id="appleBox">
<div>🍎</div>
<div class="fruit-count" id="appleCount">0</div>
</div>
<div class="fruit-box" id="bananaBox">
<div>🍌</div>
<div class="fruit-count" id="bananaCount">0</div>
</div>
<div class="fruit-box" id="grapeBox">
<div>🍇</div>
<div class="fruit-count" id="grapeCount">0</div>
</div>
<div class="fruit-box" id="strawberryBox">
<div>🍓</div>
<div class="fruit-count" id="strawberryCount">0</div>
</div>
</div>
<div class="controls">
<button class="btn btn-start" id="startBtn" onclick="startGame()">🎮 Start</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<div class="game-over" id="gameOver">
<h2 id="winnerText">🎉 Gewonnen!</h2>
<button class="btn btn-start" onclick="resetGame()">🔄 Nochmal</button>
</div>
<script>
const FRUITS = ['🍎', '🍌', '🍇', '🍓'];
const DIFFICULTY = {
baby: { time: 4000, showHint: true, showCounter: true, desc: '👶 Baby: 4 Sekunden + große Hilfen' },
easy: { time: 2500, showHint: true, showCounter: true, desc: '🟢 Einfach: 2.5 Sekunden + Hilfen' },
medium: { time: 1500, showHint: false, showCounter: true, desc: '🟡 Mittel: 1.5 Sekunden + Zähler' },
hard: { time: 800, showHint: false, showCounter: false, desc: '🔴 Schwer: 0.8 Sekunden, alles selbst!' }
};
let difficulty = 'baby';
let playerDeck = [], botDeck = [];
let playerCard = null, botCard = null;
let gameRunning = false, canRing = false;
let botTimer = null;
function setDifficulty(diff) {
difficulty = diff;
document.querySelectorAll('.diff-btn').forEach(b => b.classList.remove('active'));
event.target.classList.add('active');
document.getElementById('diffDesc').textContent = DIFFICULTY[diff].desc;
}
function createDeck() {
const deck = [];
FRUITS.forEach(fruit => {
for (let i = 1; i <= 5; i++) deck.push({ fruit, num: i });
});
// Doppelte Menge für 40 Karten insgesamt
return shuffle([...deck, ...deck]);
}
function shuffle(a) {
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 startGame() {
const deck = createDeck();
playerDeck = deck.slice(0, 20);
botDeck = deck.slice(20);
gameRunning = true; canRing = false;
document.getElementById('startBtn').style.display = 'none';
document.getElementById('diffSelect').style.display = 'none';
document.getElementById('diffDesc').style.display = 'none';
document.getElementById('fruitCounter').style.display =
DIFFICULTY[difficulty].showCounter ? 'flex' : 'none';
updateDisplay();
nextTurn();
}
function nextTurn() {
if (!gameRunning) return;
if (playerDeck.length === 0) { endGame(false); return; }
if (botDeck.length === 0) { endGame(true); return; }
// Verstecke Hinweise
document.getElementById('playerHint').style.display = 'none';
document.getElementById('bellHint').style.display = 'none';
document.querySelectorAll('.fruit-box').forEach(b => b.classList.remove('highlight'));
document.querySelectorAll('.card-display').forEach(c => c.classList.remove('match'));
playerCard = playerDeck.shift();
botCard = botDeck.shift();
updateDisplay();
updateCounters();
const counts = { '🍎': 0, '🍌': 0, '🍇': 0, '🍓': 0 };
if (playerCard) counts[playerCard.fruit] += playerCard.num;
if (botCard) counts[botCard.fruit] += botCard.num;
const hasFive = Object.values(counts).includes(5);
if (hasFive) {
canRing = true;
document.getElementById('status').textContent = '🔔 FÜNF! Schnell klingeln!';
document.getElementById('status').style.color = '#FFD700';
// Visuelle Hilfen für Kinder
if (DIFFICULTY[difficulty].showHint) {
document.getElementById('playerHint').style.display = 'block';
document.getElementById('bellHint').style.display = 'block';
document.getElementById('playerCard').classList.add('match');
document.getElementById('botCard').classList.add('match');
// Highlight richtige Frucht
for (let [fruit, count] of Object.entries(counts)) {
if (count === 5) {
const boxId = fruit === '🍎' ? 'appleBox' :
fruit === '🍌' ? 'bananaBox' :
fruit === '🍇' ? 'grapeBox' : 'strawberryBox';
document.getElementById(boxId).classList.add('highlight');
}
}
}
// Bot reagiert
botTimer = setTimeout(() => {
if (gameRunning && canRing) botRing();
}, DIFFICULTY[difficulty].time);
} else {
canRing = false;
document.getElementById('status').textContent = 'Keine 5 gleichen... Nächste Karte!';
document.getElementById('status').style.color = 'white';
setTimeout(nextTurn, 1500);
}
}
function updateCounters() {
const counts = { '🍎': 0, '🍌': 0, '🍇': 0, '🍓': 0 };
if (playerCard) counts[playerCard.fruit] += playerCard.num;
if (botCard) counts[botCard.fruit] += botCard.num;
document.getElementById('appleCount').textContent = counts['🍎'];
document.getElementById('bananaCount').textContent = counts['🍌'];
document.getElementById('grapeCount').textContent = counts['🍇'];
document.getElementById('strawberryCount').textContent = counts['🍓'];
}
function updateDisplay() {
document.getElementById('playerCards').textContent = playerDeck.length;
document.getElementById('botCards').textContent = botDeck.length;
document.getElementById('playerCount').textContent = (playerDeck.length + 1) + ' Karten';
document.getElementById('botCount').textContent = (botDeck.length + 1) + ' Karten';
if (playerCard) {
document.getElementById('playerEmoji').textContent = playerCard.fruit;
document.getElementById('playerNum').textContent = playerCard.num;
}
if (botCard) {
document.getElementById('botEmoji').textContent = botCard.fruit;
document.getElementById('botNum').textContent = botCard.num;
}
}
function ringBell() {
if (!canRing || !gameRunning) {
// Falsch geklingelt - Strafe!
if (gameRunning) {
document.getElementById('status').textContent = '❌ Zu früh! Computer bekommt deine Karten!';
botDeck.push(...playerDeck.splice(0, Math.min(3, playerDeck.length)));
updateDisplay();
setTimeout(nextTurn, 2000);
}
return;
}
clearTimeout(botTimer);
canRing = false;
document.querySelector('.bell').classList.add('ringing');
setTimeout(() => document.querySelector('.bell').classList.remove('ringing'), 300);
// Gewinner bekommt alle Karten
const wonCards = [playerCard, botCard, ...playerDeck.splice(0, 2), ...botDeck.splice(0, 2)];
playerDeck.push(...wonCards.filter(c => c));
document.getElementById('status').textContent = '🎉 Super! Du hast ' + wonCards.length + ' Karten gewonnen!';
updateDisplay();
setTimeout(nextTurn, 2000);
}
function botRing() {
if (!canRing || !gameRunning) return;
canRing = false;
document.querySelector('.bell').classList.add('ringing');
setTimeout(() => document.querySelector('.bell').classList.remove('ringing'), 300);
const wonCards = [playerCard, botCard, ...playerDeck.splice(0, 2), ...botDeck.splice(0, 2)];
botDeck.push(...wonCards.filter(c => c));
document.getElementById('status').textContent = '🤖 Computer war schneller!';
updateDisplay();
setTimeout(nextTurn, 2000);
}
function endGame(playerWon) {
gameRunning = false;
document.getElementById('winnerText').innerHTML = playerWon ?
'🎉 Du hast gewonnen!' : '🤖 Computer gewinnt!';
document.getElementById('gameOver').style.display = 'block';
}
function resetGame() {
document.getElementById('gameOver').style.display = 'none';
document.getElementById('startBtn').style.display = 'block';
document.getElementById('diffSelect').style.display = 'flex';
document.getElementById('diffDesc').style.display = 'block';
document.getElementById('fruitCounter').style.display = 'none';
document.getElementById('status').textContent = 'Wähle den Schwierigkeitsgrad!';
document.getElementById('status').style.color = 'white';
playerCard = null; botCard = null;
document.getElementById('playerEmoji').textContent = '❓';
document.getElementById('playerNum').textContent = '';
document.getElementById('botEmoji').textContent = '❓';
document.getElementById('botNum').textContent = '';
updateDisplay();
}
</script>
</body>
</html>
+471
View File
@@ -0,0 +1,471 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Klavier - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #2d1b4e 0%, #1a1a2e 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 15px;
}
h1 { color: white; margin-bottom: 5px; font-size: 24px; }
.mode-tabs { display: flex; gap: 10px; margin-bottom: 15px; }
.mode-btn {
background: rgba(255,255,255,0.2); border: 2px solid white; color: white;
padding: 8px 20px; border-radius: 20px; cursor: pointer; font-family: inherit;
}
.mode-btn.active { background: white; color: #2d1b4e; font-weight: bold; }
.practice-display {
background: rgba(255,255,255,0.1); border-radius: 15px; padding: 15px;
margin: 10px 0; color: white; min-height: 80px; width: 350px; text-align: center;
}
.practice-note {
font-size: 48px; display: inline-block; margin: 0 5px; transition: all 0.3s;
}
.practice-note.highlight {
transform: scale(1.3); text-shadow: 0 0 20px #4ade80;
animation: bounce 0.5s infinite alternate;
}
@keyframes bounce { from { transform: scale(1.3); } to { transform: scale(1.5); } }
.practice-note.correct { color: #4ade80; }
.practice-note.wrong { color: #ff6b6b; }
.piano {
display: flex; position: relative; margin: 15px 0;
background: #333; padding: 10px; border-radius: 10px;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
}
.white-key {
width: 45px; height: 180px; background: white;
border: 2px solid #333; border-radius: 0 0 5px 5px;
display: flex; flex-direction: column; justify-content: flex-end; align-items: center;
padding-bottom: 8px; cursor: pointer; font-size: 12px; color: #666;
transition: all 0.1s;
}
.white-key:active, .white-key.active, .white-key.highlight {
background: #ddd; transform: translateY(2px);
}
.white-key.highlight { background: #feca57 !important; }
.white-key.correct { background: #86efac !important; }
.black-key {
width: 28px; height: 110px; background: linear-gradient(180deg, #333 0%, #000 100%);
border-radius: 0 0 3px 3px; position: absolute; top: 10px;
display: flex; flex-direction: column; justify-content: flex-end; align-items: center;
padding-bottom: 6px; cursor: pointer; color: #aaa; font-size: 9px;
transition: all 0.1s; z-index: 10;
}
.black-key:active, .black-key.active, .black-key.highlight {
background: #222; transform: translateY(2px);
}
.black-key.highlight { background: #feca57 !important; }
.black-1 { left: 36px; } .black-2 { left: 81px; }
.black-3 { left: 171px; } .black-4 { left: 216px; }
.black-5 { left: 261px; }
.songs-grid {
display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px;
max-width: 400px; margin: 10px 0;
}
.song-card {
background: rgba(255,255,255,0.1); border: 2px solid white; border-radius: 15px;
padding: 15px; color: white; cursor: pointer; transition: all 0.2s; text-align: center;
}
.song-card:hover { background: rgba(255,255,255,0.3); transform: translateY(-3px); }
.song-card:disabled { opacity: 0.5; cursor: not-allowed; }
.song-emoji { font-size: 30px; margin-bottom: 5px; }
.song-title { font-weight: bold; font-size: 14px; }
.song-desc { font-size: 11px; color: #aaa; margin-top: 5px; }
.exercise-controls {
display: flex; gap: 10px; margin: 15px 0; flex-wrap: wrap; justify-content: center;
}
.btn {
background: white; border: none; border-radius: 10px; padding: 10px 20px;
font-size: 14px; cursor: pointer; font-family: inherit; font-weight: bold;
}
.btn-start { background: #4ade80; color: white; }
.btn-hint { background: #feca57; color: #333; }
.btn-stop { background: #ff6b6b; color: white; }
.btn-back { background: #6b7280; color: white; text-decoration: none; }
.progress {
width: 350px; height: 10px; background: rgba(255,255,255,0.2); border-radius: 5px;
margin: 10px 0; overflow: hidden;
}
.progress-bar {
height: 100%; background: linear-gradient(90deg, #4ade80, #22c55e);
width: 0%; transition: width 0.3s;
}
.feedback { font-size: 24px; margin: 10px 0; min-height: 30px; }
</style>
</head>
<body>
<h1>🎹 Klavier</h1>
<div class="mode-tabs">
<button class="mode-btn active" id="freeBtn" onclick="setMode('free')">🎵 Frei spielen</button>
<button class="mode-btn" id="practiceBtn" onclick="setMode('practice')">📚 Üben</button>
</div>
<!-- Übungs-Display -->
<div class="practice-display" id="practiceDisplay" style="display:none">
<div id="practiceNotes">Wähle ein Lied zum Üben!</div>
<div class="progress"><div class="progress-bar" id="progressBar"></div></div>
<div class="feedback" id="feedback"></div>
</div>
<!-- Lieder-Auswahl -->
<div class="songs-grid" id="songsGrid">
<div class="song-card" onclick="selectSong('twinkle')">
<div class="song-emoji"></div>
<div class="song-title">Twinkle Twinkle</div>
<div class="song-desc">C-C-G-G-A-A-G</div>
</div>
<div class="song-card" onclick="selectSong('entchen')">
<div class="song-emoji">🦆</div>
<div class="song-title">Alle meine Entchen</div>
<div class="song-desc">C-D-E-F-G-G</div>
</div>
<div class="song-card" onclick="selectSong('haenschen')">
<div class="song-emoji">🐰</div>
<div class="song-title">Hänschen klein</div>
<div class="song-desc">G-E-E-F-D-D-C</div>
</div>
<div class="song-card" onclick="selectSong('happy')">
<div class="song-emoji">🎂</div>
<div class="song-title">Happy Birthday</div>
<div class="song-desc">C-C-D-C-F-E</div>
</div>
<div class="song-card" onclick="selectSong('mary')">
<div class="song-emoji">🐑</div>
<div class="song-title">Mary Had a Lamb</div>
<div class="song-desc">E-D-C-D-E-E-E</div>
</div>
<div class="song-card" onclick="selectSong('schlaf')">
<div class="song-emoji">🌙</div>
<div class="song-title">Schlaf Kindlein</div>
<div class="song-desc">G-G-E-E-F-F-D</div>
</div>
</div>
<!-- Übungs-Controls -->
<div class="exercise-controls" id="practiceControls" style="display:none">
<button class="btn btn-start" onclick="startPractice()">▶️ Start</button>
<button class="btn btn-hint" onclick="showHint()">💡 Tipp</button>
<button class="btn btn-stop" onclick="stopPractice()">⏹️ Stop</button>
</div>
<!-- Piano -->
<div class="piano" id="piano">
<div class="white-key" data-note="C4" data-key="a">C<br><span style="font-size:9px;color:#999">A</span></div>
<div class="white-key" data-note="D4" data-key="s">D<br><span style="font-size:9px;color:#999">S</span></div>
<div class="white-key" data-note="E4" data-key="d">E<br><span style="font-size:9px;color:#999">D</span></div>
<div class="white-key" data-note="F4" data-key="f">F<br><span style="font-size:9px;color:#999">F</span></div>
<div class="white-key" data-note="G4" data-key="g">G<br><span style="font-size:9px;color:#999">G</span></div>
<div class="white-key" data-note="A4" data-key="h">A<br><span style="font-size:9px;color:#999">H</span></div>
<div class="white-key" data-note="B4" data-key="j">B<br><span style="font-size:9px;color:#999">J</span></div>
<div class="white-key" data-note="C5" data-key="k">C<br><span style="font-size:9px;color:#999">K</span></div>
<div class="black-key black-1" data-note="C#4" data-key="w">C#<br><span style="font-size:8px">W</span></div>
<div class="black-key black-2" data-note="D#4" data-key="e">D#<br><span style="font-size:8px">E</span></div>
<div class="black-key black-3" data-note="F#4" data-key="t">F#<br><span style="font-size:8px">T</span></div>
<div class="black-key black-4" data-note="G#4" data-key="z">G#<br><span style="font-size:8px">Z</span></div>
<div class="black-key black-5" data-note="A#4" data-key="u">A#<br><span style="font-size:8px">U</span></div>
</div>
<div class="exercise-controls">
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<script>
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const frequencies = {
'C4': 261.63, 'C#4': 277.18, 'D4': 293.66, 'D#4': 311.13,
'E4': 329.63, 'F4': 349.23, 'F#4': 369.99, 'G4': 392.00,
'G#4': 415.30, 'A4': 440.00, 'A#4': 466.16, 'B4': 493.88, 'C5': 523.25
};
const noteEmojis = {
'C4': '🔴', 'C#4': '⚫', 'D4': '🟠', 'D#4': '⚫',
'E4': '🟡', 'F4': '🟢', 'F#4': '⚫', 'G4': '🔵',
'G#4': '⚫', 'A4': '🟣', 'A#4': '⚫', 'B4': '⚪', 'C5': '🔴'
};
const songs = {
twinkle: {
notes: ['C4', 'C4', 'G4', 'G4', 'A4', 'A4', 'G4', 'F4', 'F4', 'E4', 'E4', 'D4', 'D4', 'C4'],
durations: [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 1, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 1]
},
entchen: {
notes: ['C4', 'D4', 'E4', 'F4', 'G4', 'G4', 'A4', 'A4', 'A4', 'A4', 'G4', 'F4', 'F4', 'F4', 'F4', 'E4', 'E4', 'D4', 'D4', 'D4', 'D4', 'C4'],
durations: [0.4, 0.4, 0.4, 0.4, 0.6, 0.6, 0.3, 0.3, 0.3, 0.3, 0.8, 0.3, 0.3, 0.3, 0.3, 0.4, 0.4, 0.3, 0.3, 0.3, 0.3, 0.8]
},
haenschen: {
notes: ['G4', 'E4', 'E4', 'F4', 'D4', 'D4', 'C4', 'G4', 'G4', 'E4', 'E4', 'F4', 'D4', 'D4', 'C4'],
durations: [0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.8, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.8]
},
happy: {
notes: ['C4', 'C4', 'D4', 'C4', 'F4', 'E4', 'C4', 'C4', 'D4', 'C4', 'G4', 'F4'],
durations: [0.4, 0.4, 0.4, 0.4, 0.4, 0.8, 0.4, 0.4, 0.4, 0.4, 0.4, 0.8]
},
mary: {
notes: ['E4', 'D4', 'C4', 'D4', 'E4', 'E4', 'E4', 'D4', 'D4', 'D4', 'E4', 'G4', 'G4'],
durations: [0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.6, 0.4, 0.4, 0.6, 0.4, 0.4, 0.6]
},
schlaf: {
notes: ['G4', 'G4', 'E4', 'E4', 'F4', 'F4', 'D4', 'G4', 'G4', 'E4', 'E4', 'f4', 'f4', 'd4'],
durations: [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 1, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 1]
}
};
let currentMode = 'free';
let currentSong = null;
let currentNoteIndex = 0;
let practiceActive = false;
let waitingForNote = false;
function setMode(mode) {
currentMode = mode;
document.querySelectorAll('.mode-btn').forEach(b => b.classList.remove('active'));
document.getElementById(mode + 'Btn').classList.add('active');
if (mode === 'practice') {
document.getElementById('practiceDisplay').style.display = 'block';
document.getElementById('practiceControls').style.display = 'flex';
} else {
document.getElementById('practiceDisplay').style.display = 'none';
document.getElementById('practiceControls').style.display = 'none';
stopPractice();
}
}
function selectSong(songName) {
currentSong = songName;
const song = songs[songName];
// Noten anzeigen
const display = document.getElementById('practiceNotes');
display.innerHTML = song.notes.map((note, i) =>
`<span class="practice-note" id="note-${i}">${noteEmojis[note]}</span>`
).join(' ');
currentNoteIndex = 0;
updateProgress();
// Demo abspielen
playSong(songName);
}
function startPractice() {
if (!currentSong) {
document.getElementById('feedback').textContent = '❌ Wähle zuerst ein Lied!';
return;
}
practiceActive = true;
currentNoteIndex = 0;
highlightNextNote();
document.getElementById('feedback').textContent = '🎵 Spiel die markierte Note!';
}
function highlightNextNote() {
if (!practiceActive || !currentSong) return;
const song = songs[currentSong];
// Alte Markierung entfernen
document.querySelectorAll('.practice-note').forEach(n => {
n.classList.remove('highlight', 'correct', 'wrong');
});
if (currentNoteIndex >= song.notes.length) {
// Fertig!
document.getElementById('feedback').textContent = '🎉 Super! Lied geschafft!';
playNote('C4', 0.3);
setTimeout(() => playNote('E4', 0.3), 200);
setTimeout(() => playNote('G4', 0.5), 400);
practiceActive = false;
return;
}
// Nächste Note markieren
const noteEl = document.getElementById('note-' + currentNoteIndex);
if (noteEl) noteEl.classList.add('highlight');
// Taste auf Piano markieren
const expectedNote = song.notes[currentNoteIndex];
highlightKey(expectedNote);
waitingForNote = true;
updateProgress();
}
function highlightKey(note) {
document.querySelectorAll('.white-key, .black-key').forEach(k => {
k.classList.remove('highlight');
});
const key = document.querySelector(`[data-note="${note}"]`);
if (key) key.classList.add('highlight');
}
function checkPracticeNote(playedNote) {
if (!practiceActive || !waitingForNote || !currentSong) return false;
const song = songs[currentSong];
const expectedNote = song.notes[currentNoteIndex];
const noteEl = document.getElementById('note-' + currentNoteIndex);
if (playedNote === expectedNote) {
// Richtig!
noteEl.classList.remove('highlight');
noteEl.classList.add('correct');
document.getElementById('feedback').textContent = '✅ Richtig!';
currentNoteIndex++;
waitingForNote = false;
setTimeout(() => highlightNextNote(), 500);
return true;
} else {
// Falsch
noteEl.classList.add('wrong');
document.getElementById('feedback').textContent = '❌ Das war ' + playedNote + ', versuche ' + expectedNote;
setTimeout(() => {
noteEl.classList.remove('wrong');
if (practiceActive) highlightNextNote();
}, 1000);
return false;
}
}
function showHint() {
if (!practiceActive || !currentSong) return;
const song = songs[currentSong];
const expectedNote = song.notes[currentNoteIndex];
// Note blinkt
const key = document.querySelector(`[data-note="${expectedNote}"]`);
if (key) {
key.style.animation = 'bounce 0.5s 3';
setTimeout(() => key.style.animation = '', 1500);
}
document.getElementById('feedback').textContent = '💡 Schau auf die gelbe Taste!';
}
function stopPractice() {
practiceActive = false;
waitingForNote = false;
currentNoteIndex = 0;
document.querySelectorAll('.practice-note').forEach(n => {
n.classList.remove('highlight', 'correct', 'wrong');
});
document.querySelectorAll('.white-key, .black-key').forEach(k => {
k.classList.remove('highlight');
});
document.getElementById('progressBar').style.width = '0%';
document.getElementById('feedback').textContent = '';
}
function updateProgress() {
if (!currentSong) return;
const song = songs[currentSong];
const progress = (currentNoteIndex / song.notes.length) * 100;
document.getElementById('progressBar').style.width = progress + '%';
}
function playNote(note, duration = 0.3) {
if (!frequencies[note]) return;
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.frequency.value = frequencies[note];
osc.type = 'sine';
gain.gain.setValueAtTime(0.3, audioCtx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + duration);
osc.start();
osc.stop(audioCtx.currentTime + duration);
// Visuelles Feedback
const key = document.querySelector(`[data-note="${note}"]`);
if (key) {
key.classList.add('active');
setTimeout(() => key.classList.remove('active'), 200);
}
}
async function playSong(songName) {
const song = songs[songName];
if (!song) return;
for (let i = 0; i < song.notes.length; i++) {
playNote(song.notes[i], song.durations[i]);
await sleep(song.durations[i] * 1000);
}
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Tasten-Events
document.querySelectorAll('.white-key, .black-key').forEach(key => {
key.addEventListener('mousedown', () => {
const note = key.dataset.note;
playNote(note);
if (currentMode === 'practice') {
checkPracticeNote(note);
}
});
key.addEventListener('touchstart', (e) => {
e.preventDefault();
const note = key.dataset.note;
playNote(note);
if (currentMode === 'practice') {
checkPracticeNote(note);
}
});
});
// Keyboard
document.addEventListener('keydown', (e) => {
const key = document.querySelector(`[data-key="${e.key.toLowerCase()}"]`);
if (key && !key.classList.contains('active')) {
const note = key.dataset.note;
playNote(note);
key.classList.add('active');
if (currentMode === 'practice') {
checkPracticeNote(note);
}
}
});
document.addEventListener('keyup', (e) => {
const key = document.querySelector(`[data-key="${e.key.toLowerCase()}"]`);
if (key) key.classList.remove('active');
});
</script>
</body>
</html>
+387
View File
@@ -0,0 +1,387 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Leiterspiel - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 10px;
}
h1 { color: white; margin-bottom: 10px; font-size: 22px; }
.board-container {
background: #8B7355; padding: 10px; border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
}
.board {
display: grid; grid-template-columns: repeat(10, 30px); gap: 2px;
}
.cell {
width: 30px; height: 30px; border-radius: 4px;
display: flex; align-items: center; justify-content: center;
font-size: 9px; font-weight: bold; position: relative;
}
/* Schachbrett-Muster */
.cell:nth-child(even) { background: #F5DEB3; }
.cell:nth-child(odd) { background: #DEB887; }
.cell.start { background: #90EE90 !important; }
.cell.finish { background: #FFD700 !important; border: 2px solid #FFA500; }
.cell.ladder { background: #87CEEB !important; }
.cell.snake { background: #ff9999 !important; }
.cell-number { position: absolute; top: 2px; left: 2px; font-size: 7px; }
.piece {
width: 16px; height: 16px; border-radius: 50%;
position: absolute; border: 1px solid white;
box-shadow: 1px 1px 3px rgba(0,0,0,0.5);
z-index: 10; font-size: 8px; display: flex; align-items: center; justify-content: center;
}
.piece.p1 { background: #ff4444; color: white; }
.piece.p2 { background: #4444ff; color: white; }
/* Emoji auf Feldern */
.cell-icon { font-size: 14px; }
/* UI */
.turn-indicator {
background: rgba(255,255,255,0.2); padding: 10px 20px; border-radius: 20px;
color: white; margin: 10px 0; font-size: 16px;
}
.status { color: white; font-size: 14px; margin: 10px 0; text-align: center; max-width: 320px; }
/* Würfel */
.dice-area { margin: 15px 0; }
.dice {
width: 60px; height: 60px; background: white; border-radius: 10px;
display: flex; align-items: center; justify-content: center;
font-size: 30px; cursor: pointer; border: 3px solid #333;
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
}
.dice.rolling { animation: shake 0.5s ease-in-out; }
@keyframes shake {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(-10deg); }
75% { transform: rotate(10deg); }
}
.controls { display: flex; gap: 10px; margin-top: 15px; }
.btn {
background: white; border: none; border-radius: 10px; padding: 10px 20px;
font-size: 14px; cursor: pointer; font-family: inherit; font-weight: bold;
}
.btn-roll { background: #4ade80; color: white; }
.btn-back { background: #ff6b6b; color: white; text-decoration: none; }
.legend {
display: flex; gap: 15px; margin: 10px 0; flex-wrap: wrap; justify-content: center;
}
.legend-item { display: flex; align-items: center; gap: 5px; color: white; font-size: 12px; }
.legend-box { width: 18px; height: 18px; border-radius: 3px; }
.winner {
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: white; padding: 30px; border-radius: 20px;
text-align: center; display: none; z-index: 100;
box-shadow: 0 10px 50px rgba(0,0,0,0.5);
}
.winner h2 { margin-bottom: 15px; }
.winner-emoji { font-size: 40px; }
</style>
</head>
<body>
<h1>🐍 Leiterspiel 🪜</h1>
<div class="turn-indicator" id="turnIndicator">
🔴 Spieler 1 ist dran!
</div>
<div class="board-container">
<div class="board" id="board"></div>
</div>
<div class="legend">
<div class="legend-item">
<div class="legend-box" style="background:#87CEEB"></div>
🪜 Leiter
</div>
<div class="legend-item">
<div class="legend-box" style="background:#ff9999"></div>
🐍 Schlange
</div>
</div>
<div class="status" id="status">Würfle und klettere hoch! Aber pass auf vor den Schlangen! 🐍</div>
<div class="dice-area">
<div class="dice" id="dice" onclick="roll()">🎲</div>
</div>
<div class="controls">
<button class="btn btn-roll" onclick="roll()">🎲 Würfeln</button>
<button class="btn" onclick="resetGame()">🔄 Neustart</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<div class="winner" id="winner">
<h2>🎉 Gewinner! 🎉</h2>
<div class="winner-emoji" id="winnerText"></div>
<button class="btn btn-roll" onclick="resetGame()" style="margin-top: 15px">🔄 Nochmal</button>
</div>
<script>
// Leitern: von -> nach oben
const LADDERS = {
4: 14, // 4 -> 14
9: 31, // 9 -> 31
20: 38, // 20 -> 38
28: 84, // 28 -> 84
40: 59, // 40 -> 59
51: 67, // 51 -> 67
63: 81, // 63 -> 81
71: 91 // 71 -> 91
};
// Schlangen: von -> nach unten
const SNAKES = {
17: 7, // 17 -> 7
54: 34, // 54 -> 34
62: 19, // 62 -> 19
64: 60, // 64 -> 60
87: 24, // 87 -> 24
93: 73, // 93 -> 73
95: 75, // 95 -> 75
99: 78 // 99 -> 78
};
let positions = [1, 1]; // Spieler 1 und 2
let currentPlayer = 0; // 0 = Spieler 1, 1 = Spieler 2
let rolling = false;
let gameEnded = false;
function initBoard() {
const board = document.getElementById('board');
board.innerHTML = '';
// Brett: Zeile 10 (100-91), Zeile 9 (81-90), etc.
for (let row = 9; row >= 0; row--) {
const isEvenRow = row % 2 === 0;
const startNum = row * 10 + 1;
const endNum = (row + 1) * 10;
for (let i = 0; i < 10; i++) {
const num = isEvenRow ? startNum + i : endNum - i;
const cell = document.createElement('div');
cell.className = 'cell';
cell.dataset.num = num;
// Start
if (num === 1) {
cell.classList.add('start');
cell.innerHTML = '<span class="cell-icon">🏠</span>';
}
// Ziel
else if (num === 100) {
cell.classList.add('finish');
cell.innerHTML = '<span class="cell-icon">🏆</span>';
}
// Leiter
else if (LADDERS[num]) {
cell.classList.add('ladder');
cell.innerHTML = '<span class="cell-icon">🪜</span>';
cell.title = 'Nach ' + LADDERS[num];
}
// Schlange
else if (SNAKES[num]) {
cell.classList.add('snake');
cell.innerHTML = '<span class="cell-icon">🐍</span>';
cell.title = 'Nach ' + SNAKES[num];
}
else {
cell.innerHTML = '<span class="cell-number">' + num + '</span>';
}
board.appendChild(cell);
}
}
updatePieces();
}
function updatePieces() {
// Alte Figuren entfernen
document.querySelectorAll('.piece').forEach(p => p.remove());
// Neue Figuren setzen
positions.forEach((pos, idx) => {
const cell = document.querySelector(`[data-num="${pos}"]`);
if (cell) {
const piece = document.createElement('div');
piece.className = 'piece p' + (idx + 1);
piece.textContent = (idx + 1);
// Position innerhalb der Zelle
if (idx === 0) {
piece.style.top = '2px';
piece.style.left = '2px';
} else {
piece.style.bottom = '2px';
piece.style.right = '2px';
}
cell.appendChild(piece);
}
});
}
function roll() {
if (rolling || gameEnded) return;
rolling = true;
const dice = document.getElementById('dice');
dice.classList.add('rolling');
// Zufällige Zahlen zeigen während des Rollens
let rollInterval = setInterval(() => {
dice.textContent = Math.floor(Math.random() * 6) + 1;
}, 100);
setTimeout(() => {
clearInterval(rollInterval);
dice.classList.remove('rolling');
const result = Math.floor(Math.random() * 6) + 1;
dice.textContent = result;
movePlayer(result);
setTimeout(() => {
rolling = false;
}, 600);
}, 500);
}
function movePlayer(rollNum) {
const playerName = currentPlayer === 0 ? '🔴 Spieler 1' : '🔵 Spieler 2';
let currentPos = positions[currentPlayer];
let newPos = currentPos + rollNum;
const status = document.getElementById('status');
// Über 100?
if (newPos > 100) {
status.textContent = playerName + ' muss genau auf 100 kommen! Bleibt auf ' + currentPos;
speak(playerName + ' bleibt auf ' + currentPos);
switchPlayer();
return;
}
// Bewegen
status.textContent = playerName + ' würfelt ' + rollNum + ' und geht zu Feld ' + newPos;
speak(playerName.replace('🔴', '').replace('🔵', '') + ' zieht auf Feld ' + newPos);
positions[currentPlayer] = newPos;
updatePieces();
// Prüfen auf Leiter oder Schlange
setTimeout(() => {
checkSpecial(newPos);
}, 500);
}
function checkSpecial(pos) {
const status = document.getElementById('status');
const playerName = currentPlayer === 0 ? '🔴 Spieler 1' : '🔵 Spieler 2';
// Leiter?
if (LADDERS[pos]) {
const target = LADDERS[pos];
positions[currentPlayer] = target;
status.innerHTML = '🎉 ' + playerName + ' findet eine 🪜 Leiter! Hoch zu Feld ' + target + '!';
speak('Leiter! Hoch zu Feld ' + target);
updatePieces();
checkWin();
}
// Schlange?
else if (SNAKES[pos]) {
const target = SNAKES[pos];
positions[currentPlayer] = target;
status.innerHTML = '😱 ' + playerName + ' trifft auf eine 🐍 Schlange! Runter zu Feld ' + target + '!';
speak('Schlange! Runter zu Feld ' + target);
updatePieces();
checkWin();
}
// Gewonnen?
else if (pos === 100) {
checkWin();
}
else {
switchPlayer();
}
}
function checkWin() {
if (positions[currentPlayer] === 100) {
gameEnded = true;
const playerName = currentPlayer === 0 ? '🔴 Spieler 1' : '🔵 Spieler 2';
document.getElementById('winnerText').innerHTML = playerName + '<br><small>hat gewonnen! 🏆</small>';
document.getElementById('winner').style.display = 'block';
speak(playerName + ' hat gewonnen!');
} else {
switchPlayer();
}
}
function switchPlayer() {
currentPlayer = currentPlayer === 0 ? 1 : 0;
const indicator = document.getElementById('turnIndicator');
if (currentPlayer === 0) {
indicator.innerHTML = '🔴 Spieler 1 ist dran!';
} else {
indicator.innerHTML = '🔵 Spieler 2 ist dran!';
}
}
function resetGame() {
positions = [1, 1];
currentPlayer = 0;
gameEnded = false;
rolling = false;
document.getElementById('dice').textContent = '🎲';
document.getElementById('status').textContent = 'Würfle und klettere hoch! Aber pass auf vor den Schlangen! 🐍';
document.getElementById('turnIndicator').innerHTML = '🔴 Spieler 1 ist dran!';
document.getElementById('winner').style.display = 'none';
initBoard();
}
function speak(text) {
if ('speechSynthesis' in window) {
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'de-DE';
speechSynthesis.speak(utterance);
}
}
// Init
initBoard();
</script>
</body>
</html>
+661
View File
@@ -0,0 +1,661 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Mensch ärgere dich nicht - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #FEF3C7 0%, #FDE68A 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 10px;
}
h1 { color: #92400E; margin-bottom: 5px; font-size: 22px; text-align: center; }
.setup-screen {
background: white; padding: 20px; border-radius: 15px; max-width: 400px;
width: 95%; box-shadow: 0 10px 30px rgba(0,0,0,0.2);
}
.setup-screen h2 { color: #333; margin-bottom: 15px; text-align: center; font-size: 20px; }
.player-setup { margin: 12px 0; }
.player-label {
display: flex; align-items: center; padding: 8px 12px; border-radius: 8px;
font-weight: bold; margin-bottom: 5px; font-size: 14px;
}
.player-label.red { background: #FEE2E2; color: #991B1B; }
.player-label.blue { background: #DBEAFE; color: #1E40AF; }
.player-label.green { background: #D1FAE5; color: #065F46; }
.player-label.yellow { background: #FEF3C7; color: #92400E; }
.player-setup input[type="text"] {
width: 100%; padding: 10px; border: 2px solid #E5E7EB; border-radius: 8px;
font-family: inherit; font-size: 14px;
}
.player-setup input.name-red { border-color: #EF4444; }
.player-setup input.name-blue { border-color: #3B82F6; }
.player-setup input.name-green { border-color: #10B981; }
.player-setup input.name-yellow { border-color: #F59E0B; }
.checkbox-wrapper { display: flex; align-items: center; gap: 8px; margin-top: 5px; font-size: 13px; }
.checkbox-wrapper input { width: 18px; height: 18px; }
.rules-check {
background: #F3F4F6; padding: 12px; border-radius: 8px; margin: 15px 0;
}
.rules-check label { display: flex; align-items: center; gap: 8px; font-size: 14px; cursor: pointer; }
.btn {
background: #10B981; color: white; border: none; border-radius: 10px;
padding: 12px 30px; font-size: 16px; cursor: pointer; font-family: inherit; font-weight: bold;
width: 100%; margin-top: 10px;
}
.btn:hover { background: #059669; }
.btn-back { background: #EF4444; text-decoration: none; display: inline-block; text-align: center; }
/* Spielbrett */
.game-container { display: none; flex-direction: column; align-items: center; }
.board {
display: grid; grid-template-columns: repeat(11, 32px); gap: 1px;
background: #D1D5DB; padding: 8px; border-radius: 10px;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
}
.cell {
width: 32px; height: 32px; background: white; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
position: relative; font-size: 10px;
}
/* Home-Bereiche (Ecken) */
.home-red { background: #FEE2E2; }
.home-blue { background: #DBEAFE; }
.home-green { background: #D1FAE5; }
.home-yellow { background: #FEF3C7; }
/* Ziel-Bereiche */
.goal-red { background: #FECACA; }
.goal-blue { background: #BFDBFE; }
.goal-green { background: #A7F3D0; }
.goal-yellow { background: #FDE68A; }
/* Startfelder */
.start-red { background: #EF4444; }
.start-blue { background: #3B82F6; }
.start-green { background: #10B981; }
.start-yellow { background: #F59E0B; }
/* Figuren */
.piece {
width: 26px; height: 26px; border-radius: 50%; position: absolute;
border: 2px solid white; box-shadow: 2px 2px 4px rgba(0,0,0,0.4);
display: flex; align-items: center; justify-content: center;
font-size: 11px; font-weight: bold; color: white; cursor: pointer;
transition: all 0.3s;
}
.piece:hover { transform: scale(1.2); }
.piece.red { background: #DC2626; }
.piece.blue { background: #2563EB; }
.piece.green { background: #059669; }
.piece.yellow { background: #D97706; color: white; }
.piece.selected { box-shadow: 0 0 0 3px #FFD700, 0 0 15px #FFD700; }
.piece.movable { animation: pulse 1s infinite; }
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.1); }
}
/* Würfel */
.dice-area { margin: 15px 0; }
.dice {
width: 70px; height: 70px; background: white; border-radius: 12px;
display: flex; align-items: center; justify-content: center;
font-size: 40px; cursor: pointer; border: 3px solid #374151;
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
}
.dice.rolling { animation: shake 0.5s ease-in-out; }
@keyframes shake {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(-10deg); }
75% { transform: rotate(10deg); }
}
.status-bar {
background: white; padding: 12px 20px; border-radius: 25px;
margin: 10px 0; font-size: 15px; text-align: center;
box-shadow: 0 3px 10px rgba(0,0,0,0.1); max-width: 350px;
}
.current-player { font-weight: bold; font-size: 17px; }
.throw-indicator {
color: #059669; font-weight: bold; margin-top: 5px;
}
.controls { display: flex; gap: 10px; margin-top: 15px; flex-wrap: wrap; justify-content: center; }
.game-btn {
padding: 10px 20px; border: none; border-radius: 8px;
font-size: 14px; cursor: pointer; font-family: inherit; font-weight: bold;
}
.btn-roll { background: #10B981; color: white; }
.btn-skip { background: #6B7280; color: white; }
.game-over {
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: white; padding: 30px; border-radius: 20px; text-align: center;
display: none; z-index: 100; box-shadow: 0 10px 50px rgba(0,0,0,0.5);
}
.game-over h2 { margin-bottom: 15px; color: #333; }
</style>
</head>
<body>
<h1>🎲 Mensch ärgere dich nicht</h1>
<!-- Setup -->
<div class="setup-screen" id="setupScreen">
<h2>👥 Spieler einrichten</h2>
<div class="player-setup">
<div class="player-label red">🔴 Spieler 1 (Rot)</div>
<input type="text" id="name1" class="name-red" placeholder="Name..." value="Peter">
</div>
<div class="player-setup">
<div class="player-label blue">🔵 Spieler 2 (Blau)</div>
<input type="text" id="name2" class="name-blue" placeholder="Name..." value="Anna">
<div class="checkbox-wrapper">
<input type="checkbox" id="bot2" onchange="toggleBot(2)">
<label for="bot2">🤖 Computer</label>
</div>
</div>
<div class="player-setup">
<div class="player-label green">🟢 Spieler 3 (Grün)</div>
<input type="text" id="name3" class="name-green" placeholder="Name...">
<div class="checkbox-wrapper">
<input type="checkbox" id="bot3" onchange="toggleBot(3)">
<label for="bot3">🤖 Computer</label>
</div>
</div>
<div class="player-setup">
<div class="player-label yellow">🟡 Spieler 4 (Gelb)</div>
<input type="text" id="name4" class="name-yellow" placeholder="Name...">
<div class="checkbox-wrapper">
<input type="checkbox" id="bot4" onchange="toggleBot(4)">
<label for="bot4">🤖 Computer</label>
</div>
</div>
<div class="rules-check">
<label>
<input type="checkbox" id="forceKick" checked>
<span>🎯 Schmeißen ist Pflicht</span>
</label>
</div>
<button class="btn" onclick="startGame()">🎮 Spiel starten</button>
<a href="../index.html" class="btn btn-back" style="margin-top:10px;display:block">⬅️ Zurück</a>
</div>
<!-- Spiel -->
<div class="game-container" id="gameScreen">
<div class="status-bar" id="statusBar">
<div class="current-player" id="currentPlayerName">Peter ist dran!</div>
<div id="gameStatus">Würfle eine 6 zum Starten!</div>
<div class="throw-indicator" id="throwIndicator"></div>
</div>
<div class="board" id="board"></div>
<div class="dice-area">
<div class="dice" id="dice" onclick="roll()">🎲</div>
</div>
<div class="controls">
<button class="game-btn btn-roll" onclick="roll()">🎲 Würfeln</button>
<button class="game-btn btn-skip" onclick="skipTurn()">⏭️ Zug beenden</button>
<button class="game-btn" onclick="resetGame()">🔄 Neustart</button>
<a href="../index.html" class="game-btn btn-back">⬅️ Menü</a>
</div>
</div>
<div class="game-over" id="gameOver">
<h2 id="winnerText">🎉 Gewinner!</h2>
<button class="btn" onclick="resetGame()" style="width:auto">🔄 Nochmal</button>
</div>
<script>
// Spielbrett-Layout (11x11 Grid)
// Positionen: -4 bis -1 = Home, 0-39 = Weg, 40-43 = Ziel
const PLAYERS = [
{ color: 'red', name: 'Rot', emoji: '🔴', startField: 0 },
{ color: 'blue', name: 'Blau', emoji: '🔵', startField: 10 },
{ color: 'green', name: 'Grün', emoji: '🟢', startField: 20 },
{ color: 'yellow', name: 'Gelb', emoji: '🟡', startField: 30 }
];
let players = []; // { name, color, isBot, pieces: [pos, pos, pos, pos], finished }
let currentPlayer = 0;
let currentRoll = 0;
let throwsLeft = 0; // 3 Würfe wenn alle im Start
let selectedPiece = null;
let gameRunning = false;
let forceKickRule = true;
let hasRolled = false;
function toggleBot(num) {
const checkbox = document.getElementById('bot' + num);
const input = document.getElementById('name' + num);
if (checkbox.checked) {
input.value = '🤖 Computer ' + (num-1);
input.disabled = true;
} else {
input.value = '';
input.disabled = false;
}
}
function startGame() {
forceKickRule = document.getElementById('forceKick').checked;
players = [];
for (let i = 0; i < 4; i++) {
const name = document.getElementById('name' + (i+1)).value.trim();
if (name) {
players.push({
id: i,
name: name,
color: PLAYERS[i].color,
isBot: document.getElementById('bot' + (i+1))?.checked || false,
pieces: [-1, -1, -1, -1], // -1 = Home, 0-39 = Weg, 40-43 = Ziel
finished: 0
});
}
}
if (players.length < 2) {
alert('Mindestens 2 Spieler!');
return;
}
document.getElementById('setupScreen').style.display = 'none';
document.getElementById('gameScreen').style.display = 'flex';
initBoard();
currentPlayer = 0;
throwsLeft = calculateThrows();
updateStatus();
gameRunning = true;
if (players[0].isBot) setTimeout(botTurn, 1000);
}
function calculateThrows() {
const p = players[currentPlayer];
const inStart = p.pieces.filter(pos => pos === -1).length;
return (inStart === 4) ? 3 : 1;
}
function initBoard() {
const board = document.getElementById('board');
board.innerHTML = '';
// 11x11 Grid erstellen
for (let row = 0; row < 11; row++) {
for (let col = 0; col < 11; col++) {
const cell = document.createElement('div');
cell.className = 'cell';
cell.dataset.row = row;
cell.dataset.col = col;
// Home-Bereiche (Ecken)
if (row < 4 && col < 4) {
cell.classList.add('home-red');
cell.dataset.home = '0';
} else if (row < 4 && col > 6) {
cell.classList.add('home-blue');
cell.dataset.home = '1';
} else if (row > 6 && col < 4) {
cell.classList.add('home-green');
cell.dataset.home = '2';
} else if (row > 6 && col > 6) {
cell.classList.add('home-yellow');
cell.dataset.home = '3';
}
// Ziel-Bereiche
else if (row === 5 && col === 5) {
cell.style.background = '#FEF3C7'; // Mitte
}
else if (row === 5 && col >= 1 && col <= 4) {
cell.classList.add('goal-red');
}
else if (col === 5 && row >= 1 && row <= 4) {
cell.classList.add('goal-blue');
}
else if (row === 5 && col >= 6 && col <= 9) {
cell.classList.add('goal-green');
}
else if (col === 5 && row >= 6 && row <= 9) {
cell.classList.add('goal-yellow');
}
// Weg mit Startfeldern
else if (isPath(row, col)) {
const pathIdx = getPathIndex(row, col);
if (pathIdx !== null) {
// Startfelder markieren
if (pathIdx === 0) cell.classList.add('start-red');
else if (pathIdx === 10) cell.classList.add('start-blue');
else if (pathIdx === 20) cell.classList.add('start-green');
else if (pathIdx === 30) cell.classList.add('start-yellow');
cell.dataset.path = pathIdx;
}
}
board.appendChild(cell);
}
}
updatePieces();
}
function isPath(row, col) {
// Äußerer Ring + Verbindungen
return (row === 0 || row === 10 || col === 0 || col === 10 ||
(row === 4 || row === 6) && col >= 4 && col <= 6 ||
(col === 4 || col === 6) && row >= 4 && row <= 6);
}
function getPathIndex(row, col) {
// Vereinfachte Pfad-Berechnung
const paths = [
[4,0], [4,1], [4,2], [4,3], [4,4], // Rot Start
[3,4], [2,4], [1,4], [0,4], [0,5], [0,6], // Oben
[1,6], [2,6], [3,6], [4,6], // Blau Start
[4,7], [4,8], [4,9], [4,10], [5,10], [6,10], // Rechts
[6,9], [6,8], [6,7], [6,6], // Grün Start
[7,6], [8,6], [9,6], [10,6], [10,5], [10,4], // Unten
[9,4], [8,4], [7,4], [6,4], // Gelb Start
[6,3], [6,2], [6,1], [6,0], [5,0], // Links
[5,1], [5,2], [5,3] // Ziel rot
];
for (let i = 0; i < paths.length; i++) {
if (paths[i][0] === row && paths[i][1] === col) return i;
}
return null;
}
function updatePieces() {
document.querySelectorAll('.piece').forEach(p => p.remove());
players.forEach((player, pIdx) => {
player.pieces.forEach((pos, pieceIdx) => {
let cell;
if (pos === -1) {
// Home - finde freie Position
const homeCells = document.querySelectorAll(`[data-home="${pIdx}"]`);
cell = homeCells[pieceIdx] || homeCells[0];
} else if (pos >= 0 && pos < 40) {
// Auf dem Weg
const pathCells = document.querySelectorAll('[data-path]');
// Position relativ zum Startfeld
const playerStart = PLAYERS[pIdx].startField;
const adjustedPos = (pos + playerStart) % 40;
pathCells.forEach(pc => {
if (parseInt(pc.dataset.path) === adjustedPos) cell = pc;
});
} else if (pos >= 40) {
// Im Ziel
const goalPos = pos - 40;
const goalCells = document.querySelectorAll(`[class*="goal-${player.color}"]`);
cell = goalCells[goalPos];
}
if (cell) {
const piece = document.createElement('div');
piece.className = `piece ${player.color}`;
piece.textContent = pieceIdx + 1;
piece.onclick = (e) => {
e.stopPropagation();
selectPiece(pIdx, pieceIdx);
};
// Markieren wenn bewegbar
if (pIdx === currentPlayer && hasRolled && canMovePiece(pIdx, pieceIdx)) {
piece.classList.add('movable');
}
cell.appendChild(piece);
}
});
});
}
function roll() {
if (!gameRunning || hasRolled) return;
const dice = document.getElementById('dice');
dice.classList.add('rolling');
setTimeout(() => {
dice.classList.remove('rolling');
currentRoll = Math.floor(Math.random() * 6) + 1;
dice.textContent = ['⚀','⚁','⚂','⚃','⚄','⚅'][currentRoll-1];
hasRolled = true;
handleRoll(currentRoll);
}, 500);
}
function handleRoll(roll) {
const player = players[currentPlayer];
const inStart = player.pieces.filter(p => p === -1).length;
document.getElementById('gameStatus').textContent =
`${player.name} würfelt ${roll}!`;
throwsLeft--;
// Prüfen ob Zug möglich
const movable = player.pieces.map((pos, idx) => ({ pos, idx }))
.filter(({ pos, idx }) => canMove(player, idx, roll));
if (movable.length === 0) {
if (throwsLeft > 0) {
document.getElementById('gameStatus').textContent +=
` Kein Zug möglich. Noch ${throwsLeft} Versuch(e).`;
hasRolled = false;
} else {
document.getElementById('gameStatus').textContent += ' Kein Zug möglich.';
setTimeout(nextPlayer, 1500);
}
} else {
document.getElementById('gameStatus').textContent +=
` Wähle eine Figur!`;
updatePieces(); // Markiere bewegliche
if (player.isBot) {
setTimeout(() => botSelectPiece(movable), 1000);
}
}
updateThrowIndicator();
}
function canMove(player, pieceIdx, roll) {
const pos = player.pieces[pieceIdx];
if (pos === -1) {
// Starten nur mit 6
return roll === 6;
} else if (pos >= 0 && pos < 40) {
const newPos = pos + roll;
// Kann ins Ziel oder weiter
if (newPos <= 43) return true;
// Prüfen ob exakt ins Ziel
const goalEntry = 40 + (player.id * 4); // vereinfacht
return newPos <= goalEntry + 3;
}
return false;
}
function selectPiece(playerIdx, pieceIdx) {
if (playerIdx !== currentPlayer || !hasRolled) return;
const player = players[currentPlayer];
if (!canMove(player, pieceIdx, currentRoll)) {
speak('Diese Figur kann nicht bewegt werden!');
return;
}
movePiece(pieceIdx);
}
function movePiece(pieceIdx) {
const player = players[currentPlayer];
const oldPos = player.pieces[pieceIdx];
let newPos;
if (oldPos === -1) {
// Starten
newPos = 0;
} else {
newPos = oldPos + currentRoll;
}
player.pieces[pieceIdx] = newPos;
// Schmeißen?
if (forceKickRule) {
checkKick(player, newPos, pieceIdx);
}
// Gewonnen?
const inGoal = player.pieces.filter(p => p >= 40).length;
if (inGoal === 4) {
endGame(player);
return;
}
updatePieces();
if (currentRoll === 6) {
throwsLeft = calculateThrows();
hasRolled = false;
document.getElementById('gameStatus').textContent = 'Nochmal würfeln!';
} else {
setTimeout(nextPlayer, 1000);
}
}
function checkKick(player, pos, pieceIdx) {
if (pos >= 40) return; // Im Ziel nicht schmeißen
players.forEach((other, idx) => {
if (idx === currentPlayer) return;
other.pieces.forEach((otherPos, otherIdx) => {
if (otherPos === pos && otherPos >= 0 && otherPos < 40) {
other.pieces[otherIdx] = -1;
speak(`${player.name} schmeißt ${other.name} raus!`);
}
});
});
}
function botSelectPiece(movable) {
// Priorität: Kann schmeißen > ins Ziel > vorne
let best = movable[0];
movable.forEach(({ pos, idx }) => {
const newPos = pos === -1 ? 0 : pos + currentRoll;
// Kann schmeißen?
players.forEach((other, oIdx) => {
if (oIdx === currentPlayer) return;
if (other.pieces.includes(newPos)) {
best = { pos, idx };
}
});
});
movePiece(best.idx);
}
function skipTurn() {
if (!gameRunning) return;
nextPlayer();
}
function nextPlayer() {
currentPlayer = (currentPlayer + 1) % players.length;
currentRoll = 0;
hasRolled = false;
throwsLeft = calculateThrows();
selectedPiece = null;
updateStatus();
updatePieces();
if (players[currentPlayer].isBot) {
setTimeout(botTurn, 1000);
}
}
function botTurn() {
if (!gameRunning) return;
roll();
}
function updateStatus() {
const p = players[currentPlayer];
document.getElementById('currentPlayerName').textContent =
`${p.emoji} ${p.name} ist dran!`;
document.getElementById('currentPlayerName').style.color =
p.color === 'red' ? '#DC2626' :
p.color === 'blue' ? '#2563EB' :
p.color === 'green' ? '#059669' : '#D97706';
throwsLeft = calculateThrows();
updateThrowIndicator();
}
function updateThrowIndicator() {
const indicator = document.getElementById('throwIndicator');
if (throwsLeft > 1) {
indicator.textContent = `🎲 ${throwsLeft} Würfe verbleibend`;
} else if (throwsLeft === 1) {
indicator.textContent = `🎲 Letzter Wurf`;
} else {
indicator.textContent = '';
}
}
function endGame(winner) {
gameRunning = false;
document.getElementById('winnerText').innerHTML =
`🎉 ${winner.emoji} ${winner.name} hat gewonnen!`;
document.getElementById('gameOver').style.display = 'block';
}
function resetGame() {
document.getElementById('gameScreen').style.display = 'none';
document.getElementById('setupScreen').style.display = 'block';
document.getElementById('gameOver').style.display = 'none';
gameRunning = false;
}
function speak(text) {
if ('speechSynthesis' in window) {
const u = new SpeechSynthesisUtterance(text);
u.lang = 'de-DE';
speechSynthesis.speak(u);
}
}
</script>
</body>
</html>
+217
View File
@@ -0,0 +1,217 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Malen - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 10px;
}
h1 { color: white; margin: 10px 0; font-size: 24px; }
.canvas-container {
background: white; border-radius: 10px; padding: 10px;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
}
#paintCanvas {
background: white; border: 2px solid #ddd; border-radius: 5px;
cursor: crosshair; touch-action: none;
}
.tools {
display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;
margin: 15px 0; max-width: 600px;
}
.color-btn {
width: 50px; height: 50px; border: 3px solid white; border-radius: 50%;
cursor: pointer; box-shadow: 0 2px 5px rgba(0,0,0,0.2);
transition: transform 0.2s;
}
.color-btn:hover { transform: scale(1.1); }
.color-btn.active {
box-shadow: 0 0 0 4px #4ade80, 0 2px 5px rgba(0,0,0,0.2);
transform: scale(1.15);
}
.size-slider {
-webkit-appearance: none; width: 150px; height: 10px;
background: rgba(255,255,255,0.3); border-radius: 5px; outline: none;
}
.size-slider::-webkit-slider-thumb {
-webkit-appearance: none; width: 25px; height: 25px; background: white;
border-radius: 50%; cursor: pointer;
}
.tool-btn {
background: white; border: none; border-radius: 10px;
padding: 10px 20px; font-size: 20px; cursor: pointer;
}
.tool-btn.active { background: #4ade80; }
.controls { display: flex; gap: 15px; margin-top: 10px; flex-wrap: wrap; justify-content: center; }
.btn {
background: linear-gradient(135deg, #4ade80, #22c55e); border: none; border-radius: 10px;
padding: 12px 25px; font-size: 16px; cursor: pointer; font-family: inherit; font-weight: bold;
color: white;
}
.btn-back { background: #ff6b6b; text-decoration: none; }
.btn-clear { background: #feca57; }
.size-label { color: white; font-size: 14px; }
</style>
</head>
<body>
<h1>🎨 Malen</h1>
<div class="canvas-container">
<canvas id="paintCanvas" width="600" height="400"></canvas>
</div>
<div class="tools">
<button class="color-btn active" style="background: #000000;" onclick="setColor('#000000')" title="Schwarz"></button>
<button class="color-btn" style="background: #ff0000;" onclick="setColor('#ff0000')" title="Rot"></button>
<button class="color-btn" style="background: #00ff00;" onclick="setColor('#00ff00')" title="Grün"></button>
<button class="color-btn" style="background: #0000ff;" onclick="setColor('#0000ff')" title="Blau"></button>
<button class="color-btn" style="background: #ffff00;" onclick="setColor('#ffff00')" title="Gelb"></button>
<button class="color-btn" style="background: #ff00ff;" onclick="setColor('#ff00ff')" title="Magenta"></button>
<button class="color-btn" style="background: #00ffff;" onclick="setColor('#00ffff')" title="Cyan"></button>
<button class="color-btn" style="background: #ffa500;" onclick="setColor('#ffa500')" title="Orange"></button>
<button class="color-btn" style="background: #800080;" onclick="setColor('#800080')" title="Lila"></button>
<button class="color-btn" style="background: #ffffff; border: 2px solid #ccc;" onclick="setColor('#ffffff')" title="Weiß"></button>
</div>
<div style="display: flex; align-items: center; gap: 15px; flex-wrap: wrap; justify-content: center;">
<div class="size-label">Pinselgröße:</div>
<input type="range" class="size-slider" id="brushSize" min="1" max="50" value="5">
<button class="tool-btn active" id="brushBtn" onclick="setTool('brush')">🖌️ Pinsel</button>
<button class="tool-btn" id="eraserBtn" onclick="setTool('eraser')">🧹 Radiergummi</button>
</div>
<div class="controls">
<button class="btn btn-clear" onclick="clearCanvas()">🗑️ Alles löschen</button>
<button class="btn" onclick="saveImage()">💾 Speichern</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<script>
const canvas = document.getElementById('paintCanvas');
const ctx = canvas.getContext('2d');
// Responsive canvas
function resizeCanvas() {
const maxWidth = Math.min(window.innerWidth - 40, 800);
const scale = maxWidth / 600;
canvas.style.width = maxWidth + 'px';
canvas.style.height = (400 * scale) + 'px';
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// Drawing state
let isDrawing = false;
let currentColor = '#000000';
let currentSize = 5;
let currentTool = 'brush';
let lastX = 0;
let lastY = 0;
// Set canvas background to white
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
function getMousePos(e) {
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
if (e.touches) {
return {
x: (e.touches[0].clientX - rect.left) * scaleX,
y: (e.touches[0].clientY - rect.top) * scaleY
};
}
return {
x: (e.clientX - rect.left) * scaleX,
y: (e.clientY - rect.top) * scaleY
};
}
function startDrawing(e) {
isDrawing = true;
const pos = getMousePos(e);
lastX = pos.x;
lastY = pos.y;
e.preventDefault();
}
function draw(e) {
if (!isDrawing) return;
const pos = getMousePos(e);
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(pos.x, pos.y);
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.lineWidth = currentSize;
if (currentTool === 'eraser') {
ctx.strokeStyle = '#ffffff';
} else {
ctx.strokeStyle = currentColor;
}
ctx.stroke();
lastX = pos.x;
lastY = pos.y;
e.preventDefault();
}
function stopDrawing() {
isDrawing = false;
}
// Event listeners
canvas.addEventListener('mousedown', startDrawing);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', stopDrawing);
canvas.addEventListener('mouseout', stopDrawing);
canvas.addEventListener('touchstart', startDrawing);
canvas.addEventListener('touchmove', draw);
canvas.addEventListener('touchend', stopDrawing);
// Tools
function setColor(color) {
currentColor = color;
document.querySelectorAll('.color-btn').forEach(btn => btn.classList.remove('active'));
event.target.classList.add('active');
setTool('brush');
}
function setTool(tool) {
currentTool = tool;
document.getElementById('brushBtn').classList.toggle('active', tool === 'brush');
document.getElementById('eraserBtn').classList.toggle('active', tool === 'eraser');
}
document.getElementById('brushSize').addEventListener('input', (e) => {
currentSize = e.target.value;
});
function clearCanvas() {
if (confirm('Möchtest du wirklich alles löschen?')) {
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
}
function saveImage() {
const link = document.createElement('a');
link.download = 'malerei-' + new Date().toISOString().slice(0, 10) + '.png';
link.href = canvas.toDataURL();
link.click();
}
</script>
</body>
</html>
+164
View File
@@ -0,0 +1,164 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Memory - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #ff6b6b 0%, #feca57 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 10px;
}
h1 { color: white; margin: 10px 0; font-size: 24px; }
.info { color: white; font-size: 16px; margin-bottom: 10px; }
.level-select { margin-bottom: 15px; }
.level-select button {
background: white; border: none; border-radius: 8px; padding: 8px 15px;
margin: 0 5px; font-size: 14px; cursor: pointer; font-family: inherit;
}
.level-select button.active { background: #4ade80; color: white; }
#gameBoard {
display: grid; gap: 10px; margin: 10px 0;
}
.card {
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 10px; cursor: pointer; display: flex; justify-content: center; align-items: center;
font-size: 40px; transition: transform 0.3s; box-shadow: 0 4px 10px rgba(0,0,0,0.2);
}
.card:hover { transform: scale(1.05); }
.card.flipped { background: white; }
.card.matched { background: #4ade80; pointer-events: none; }
.card.hidden { font-size: 0; }
.controls { margin-top: 20px; display: flex; gap: 15px; }
.btn {
background: white; border: none; border-radius: 10px; padding: 12px 25px;
font-size: 16px; cursor: pointer; font-family: inherit; font-weight: bold;
}
.btn-back { background: #ff6b6b; color: white; text-decoration: none; }
</style>
</head>
<body>
<h1>🃏 Memory</h1>
<div class="info">Versuche: <span id="moves">0</span> | Paare: <span id="pairs">0</span>/<span id="totalPairs">0</span></div>
<div class="level-select">
<button onclick="setLevel(4)" id="lvl4">Leicht (4)</button>
<button onclick="setLevel(6)" id="lvl6">Mittel (6)</button>
<button onclick="setLevel(8)" id="lvl8">Schwer (8)</button>
<button onclick="setLevel(12)" id="lvl12">Profis (12)</button>
</div>
<div id="gameBoard"></div>
<div class="controls">
<button class="btn" onclick="initGame()">🔄 Neues Spiel</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<script>
const emojis = ['🐶','🐱','🐭','🐹','🐰','🦊','🐻','🐼','🐨','🐯','🦁','🐷','🐸','🐵','🐔','🐧','🐦','🐤','🦆','🦅','🦉','🦇','🐺','🐗','🐴','🦄'];
let cards = [];
let flippedCards = [];
let matchedPairs = 0;
let moves = 0;
let gameLocked = false;
let currentLevel = 6;
function setLevel(n) {
currentLevel = n;
document.querySelectorAll('.level-select button').forEach(b => b.classList.remove('active'));
document.getElementById('lvl' + n).classList.add('active');
initGame();
}
function initGame() {
const board = document.getElementById('gameBoard');
board.innerHTML = '';
// Berechne Grid
const totalCards = currentLevel * 2;
const cols = currentLevel <= 6 ? 4 : currentLevel <= 8 ? 4 : 6;
const rows = Math.ceil(totalCards / cols);
board.style.gridTemplateColumns = `repeat(${cols}, minmax(70px, 1fr))`;
// Wähle Emojis
const gameEmojis = emojis.slice(0, currentLevel);
cards = [...gameEmojis, ...gameEmojis].sort(() => Math.random() - 0.5);
// Erstelle Karten
cards.forEach((emoji, index) => {
const card = document.createElement('div');
card.className = 'card hidden';
card.dataset.index = index;
card.dataset.emoji = emoji;
card.textContent = emoji;
card.style.height = currentLevel >= 8 ? '80px' : '100px';
card.onclick = () => flipCard(card);
board.appendChild(card);
});
flippedCards = [];
matchedPairs = 0;
moves = 0;
gameLocked = false;
updateStats();
document.getElementById('totalPairs').textContent = currentLevel;
}
function flipCard(card) {
if (gameLocked || card.classList.contains('flipped') || card.classList.contains('matched')) return;
card.classList.remove('hidden');
card.classList.add('flipped');
flippedCards.push(card);
if (flippedCards.length === 2) {
moves++;
updateStats();
checkMatch();
}
}
function checkMatch() {
gameLocked = true;
const [card1, card2] = flippedCards;
if (card1.dataset.emoji === card2.dataset.emoji) {
setTimeout(() => {
card1.classList.add('matched');
card2.classList.add('matched');
matchedPairs++;
updateStats();
flippedCards = [];
gameLocked = false;
if (matchedPairs === currentLevel) {
setTimeout(() => alert('🎉 Gewonnen! Du hast ' + moves + ' Versuche gebraucht.'), 300);
}
}, 500);
} else {
setTimeout(() => {
card1.classList.remove('flipped');
card1.classList.add('hidden');
card2.classList.remove('flipped');
card2.classList.add('hidden');
flippedCards = [];
gameLocked = false;
}, 1000);
}
}
function updateStats() {
document.getElementById('moves').textContent = moves;
document.getElementById('pairs').textContent = matchedPairs;
}
// Init
setLevel(6);
</script>
</body>
</html>
+442
View File
@@ -0,0 +1,442 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Minesweeper - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #2d3748 0%, #1a202c 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 15px;
}
h1 { color: white; margin-bottom: 10px; font-size: 24px; }
.difficulty-select {
display: flex; gap: 10px; margin: 10px 0; flex-wrap: wrap; justify-content: center;
}
.diff-btn {
background: rgba(255,255,255,0.2); border: 2px solid white; color: white;
padding: 8px 15px; border-radius: 20px; cursor: pointer; font-family: inherit; font-size: 14px;
}
.diff-btn.active { background: white; color: #2d3748; font-weight: bold; }
.stats {
display: flex; gap: 30px; margin: 10px 0; color: white;
}
.stat { text-align: center; }
.stat-label { font-size: 12px; color: #aaa; }
.stat-value { font-size: 24px; font-weight: bold; color: #feca57; }
.board-container {
background: #718096; padding: 10px; border-radius: 10px;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
}
.board {
display: grid; gap: 2px;
}
.cell {
width: 35px; height: 35px; background: #A0AEC0;
border: 3px solid; border-color: #E2E8F0 #718096 #718096 #E2E8F0;
display: flex; align-items: center; justify-content: center;
font-size: 18px; font-weight: bold; cursor: pointer;
transition: all 0.1s;
}
.cell:hover:not(.revealed):not(.flagged) {
background: #B8C5D6;
}
.cell.revealed {
background: #E2E8F0; border: 1px solid #CBD5E0;
}
.cell.flagged {
background: #FC8181;
}
.cell.mine {
background: #E53E3E;
}
.cell.exploded {
background: #C53030;
animation: explode 0.3s ease-out;
}
@keyframes explode {
0% { transform: scale(1); }
50% { transform: scale(1.2); }
100% { transform: scale(1); }
}
.cell.number-1 { color: #3182CE; }
.cell.number-2 { color: #38A169; }
.cell.number-3 { color: #E53E3E; }
.cell.number-4 { color: #805AD5; }
.cell.number-5 { color: #B7791F; }
.cell.number-6 { color: #00B5D8; }
.cell.number-7 { color: #2D3748; }
.cell.number-8 { color: #718096; }
.instructions {
color: #ccc; margin: 10px 0; text-align: center; font-size: 14px; max-width: 350px;
}
.controls { display: flex; gap: 15px; margin-top: 20px; }
.btn {
background: white; border: none; border-radius: 10px; padding: 10px 25px;
font-size: 16px; cursor: pointer; font-family: inherit; font-weight: bold;
}
.btn-new { background: #4ade80; color: white; }
.btn-back { background: #ff6b6b; color: white; text-decoration: none; }
.game-over {
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: white; padding: 30px; border-radius: 20px; text-align: center;
display: none; z-index: 100; box-shadow: 0 10px 50px rgba(0,0,0,0.5);
}
.game-over h2 { margin-bottom: 15px; }
.game-over-emoji { font-size: 60px; }
.smiley { font-size: 40px; margin: 10px 0; cursor: pointer; }
</style>
</head>
<body>
<h1>💣 Minesweeper</h1>
<div class="difficulty-select" id="diffSelect">
<button class="diff-btn active" onclick="setDifficulty('easy')">🟢 Einfach (8×8)</button>
<button class="diff-btn" onclick="setDifficulty('medium')">🟡 Mittel (12×12)</button>
<button class="diff-btn" onclick="setDifficulty('hard')">🔴 Schwer (16×16)</button>
</div>
<div class="stats">
<div class="stat">
<div class="stat-label">Minen</div>
<div class="stat-value" id="mineCount">10</div>
</div>
<div class="stat">
<div class="stat-label">Zeit</div>
<div class="stat-value" id="timer">000</div>
</div>
</div>
<div class="smiley" id="smiley" onclick="newGame()">😊</div>
<div class="board-container">
<div class="board" id="board"></div>
</div>
<div class="instructions">
🔍 <strong>Linksklick:</strong> Feld aufdecken <br>
🚩 <strong>Rechtsklick/Langklick:></strong> Flagge setzen
</div>
<div class="controls">
<button class="btn btn-new" onclick="newGame()">🔄 Neues Spiel</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<div class="game-over" id="gameOver">
<h2 id="gameOverTitle">💥 Boom!</h2>
<div class="game-over-emoji" id="gameOverEmoji">💣</div>
<div id="gameOverText" style="margin:15px 0">Du hast eine Mine getroffen!</div>
<button class="btn btn-new" onclick="newGame()" style="margin-top:15px">🔄 Nochmal</button>
</div>
<script>
const DIFFICULTY = {
easy: { rows: 8, cols: 8, mines: 10 },
medium: { rows: 12, cols: 12, mines: 20 },
hard: { rows: 16, cols: 16, mines: 40 }
};
let difficulty = 'easy';
let board = [];
let gameRunning = false;
let timer = 0;
let timerInterval = null;
let revealedCount = 0;
let flaggedCount = 0;
let firstClick = true;
function setDifficulty(diff) {
difficulty = diff;
document.querySelectorAll('.diff-btn').forEach(b => b.classList.remove('active'));
event.target.classList.add('active');
newGame();
}
function initBoard() {
const config = DIFFICULTY[difficulty];
const boardEl = document.getElementById('board');
boardEl.innerHTML = '';
boardEl.style.gridTemplateColumns = `repeat(${config.cols}, 35px)`;
board = [];
for (let row = 0; row < config.rows; row++) {
board[row] = [];
for (let col = 0; col < config.cols; col++) {
board[row][col] = {
isMine: false,
isRevealed: false,
isFlagged: false,
neighborMines: 0
};
const cell = document.createElement('div');
cell.className = 'cell';
cell.dataset.row = row;
cell.dataset.col = col;
cell.onclick = (e) => handleClick(row, col, e);
cell.oncontextmenu = (e) => {
e.preventDefault();
handleRightClick(row, col);
};
// Touch-Handling für Flagge
let touchTimer;
cell.ontouchstart = (e) => {
touchTimer = setTimeout(() => {
e.preventDefault();
handleRightClick(row, col);
}, 500);
};
cell.ontouchend = () => clearTimeout(touchTimer);
boardEl.appendChild(cell);
}
}
updateDisplay();
}
function placeMines(excludeRow, excludeCol) {
const config = DIFFICULTY[difficulty];
let minesPlaced = 0;
while (minesPlaced < config.mines) {
const row = Math.floor(Math.random() * config.rows);
const col = Math.floor(Math.random() * config.cols);
// Nicht auf dem ersten Klick
if (row === excludeRow && col === excludeCol) continue;
// Nicht schon eine Mine
if (board[row][col].isMine) continue;
board[row][col].isMine = true;
minesPlaced++;
}
// Nachbar-Minen zählen
for (let row = 0; row < config.rows; row++) {
for (let col = 0; col < config.cols; col++) {
if (!board[row][col].isMine) {
board[row][col].neighborMines = countNeighborMines(row, col);
}
}
}
}
function countNeighborMines(row, col) {
const config = DIFFICULTY[difficulty];
let count = 0;
for (let dr = -1; dr <= 1; dr++) {
for (let dc = -1; dc <= 1; dc++) {
const newRow = row + dr;
const newCol = col + dc;
if (newRow >= 0 && newRow < config.rows &&
newCol >= 0 && newCol < config.cols) {
if (board[newRow][newCol].isMine) count++;
}
}
}
return count;
}
function handleClick(row, col, event) {
if (!gameRunning) {
if (firstClick) {
startGame(row, col);
}
return;
}
const cell = board[row][col];
if (cell.isRevealed || cell.isFlagged) return;
// Bei rechtem Mausklick (Ctrl+Klick)
if (event.ctrlKey) {
handleRightClick(row, col);
return;
}
if (cell.isMine) {
// Mine getroffen!
cell.isRevealed = true;
revealAllMines();
gameOver(false);
} else {
// Feld aufdecken
revealCell(row, col);
// Gewonnen?
const config = DIFFICULTY[difficulty];
if (revealedCount === config.rows * config.cols - config.mines) {
gameOver(true);
}
}
updateDisplay();
}
function handleRightClick(row, col) {
if (!gameRunning) return;
const cell = board[row][col];
if (cell.isRevealed) return;
cell.isFlagged = !cell.isFlagged;
flaggedCount += cell.isFlagged ? 1 : -1;
updateDisplay();
}
function revealCell(row, col) {
const config = DIFFICULTY[difficulty];
const cell = board[row][col];
if (cell.isRevealed || cell.isFlagged) return;
cell.isRevealed = true;
revealedCount++;
// Wenn 0 Nachbarn, rekursiv aufdecken
if (cell.neighborMines === 0) {
for (let dr = -1; dr <= 1; dr++) {
for (let dc = -1; dc <= 1; dc++) {
const newRow = row + dr;
const newCol = col + dc;
if (newRow >= 0 && newRow < config.rows &&
newCol >= 0 && newCol < config.cols) {
revealCell(newRow, newCol);
}
}
}
}
}
function revealAllMines() {
const config = DIFFICULTY[difficulty];
for (let row = 0; row < config.rows; row++) {
for (let col = 0; col < config.cols; col++) {
if (board[row][col].isMine) {
board[row][col].isRevealed = true;
}
}
}
}
function updateDisplay() {
const config = DIFFICULTY[difficulty];
const cells = document.querySelectorAll('.cell');
cells.forEach((cellEl, idx) => {
const row = Math.floor(idx / config.cols);
const col = idx % config.cols;
const cell = board[row][col];
cellEl.className = 'cell';
cellEl.textContent = '';
if (cell.isRevealed) {
cellEl.classList.add('revealed');
if (cell.isMine) {
cellEl.classList.add('mine');
cellEl.textContent = '💣';
} else if (cell.neighborMines > 0) {
cellEl.classList.add(`number-${cell.neighborMines}`);
cellEl.textContent = cell.neighborMines;
}
} else if (cell.isFlagged) {
cellEl.classList.add('flagged');
cellEl.textContent = '🚩';
}
});
// Minen-Zähler
document.getElementById('mineCount').textContent = config.mines - flaggedCount;
}
function startGame(firstRow, firstCol) {
gameRunning = true;
firstClick = false;
placeMines(firstRow, firstCol);
revealCell(firstRow, firstCol);
// Timer starten
timerInterval = setInterval(() => {
timer++;
document.getElementById('timer').textContent = timer.toString().padStart(3, '0');
}, 1000);
updateDisplay();
}
function gameOver(won) {
gameRunning = false;
clearInterval(timerInterval);
const modal = document.getElementById('gameOver');
const title = document.getElementById('gameOverTitle');
const emoji = document.getElementById('gameOverEmoji');
const text = document.getElementById('gameOverText');
if (won) {
title.textContent = '🎉 Gewonnen!';
emoji.textContent = '😎';
text.textContent = `Du hast alle Minen gefunden in ${timer} Sekunden!`;
document.getElementById('smiley').textContent = '😎';
speak('Glückwunsch! Du hast gewonnen!');
} else {
title.textContent = '💥 Boom!';
emoji.textContent = '💣';
text.textContent = 'Du hast eine Mine getroffen!';
document.getElementById('smiley').textContent = '😵';
speak('Oh nein! Mine getroffen!');
}
modal.style.display = 'block';
}
function newGame() {
gameRunning = false;
clearInterval(timerInterval);
timer = 0;
revealedCount = 0;
flaggedCount = 0;
firstClick = true;
document.getElementById('timer').textContent = '000';
document.getElementById('smiley').textContent = '😊';
document.getElementById('gameOver').style.display = 'none';
initBoard();
}
function speak(text) {
if ('speechSynthesis' in window) {
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'de-DE';
speechSynthesis.speak(utterance);
}
}
// Init
initBoard();
</script>
</body>
</html>
+305
View File
@@ -0,0 +1,305 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Puzzle - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 10px;
}
h1 { color: white; margin-bottom: 10px; font-size: 24px; }
.level-select { display: flex; gap: 10px; margin-bottom: 15px; }
.level-btn {
background: rgba(255,255,255,0.2); border: 2px solid white; border-radius: 10px;
padding: 8px 15px; color: white; font-size: 14px; cursor: pointer; font-family: inherit;
}
.level-btn.active { background: white; color: #f5576c; }
.image-select { display: flex; gap: 10px; margin-bottom: 15px; flex-wrap: wrap; justify-content: center; }
.img-btn {
background: white; border: 3px solid transparent; border-radius: 10px;
padding: 5px; cursor: pointer; font-size: 30px;
}
.img-btn.active { border-color: #4ade80; }
.puzzle-container {
display: grid; gap: 2px; background: rgba(0,0,0,0.2); padding: 5px;
border-radius: 10px; touch-action: none;
}
.puzzle-piece {
background-size: 300px 300px; cursor: pointer; border-radius: 5px;
transition: all 0.2s; position: relative;
}
.puzzle-piece:hover { transform: scale(1.02); }
.puzzle-piece.selected { box-shadow: 0 0 10px #4ade80; z-index: 10; }
.puzzle-piece.correct {
animation: pulse 0.5s;
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.1); }
}
.preview {
background: white; border-radius: 10px; padding: 10px; margin: 10px 0;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.preview-img { font-size: 80px; }
.controls { display: flex; gap: 15px; margin-top: 15px; flex-wrap: wrap; justify-content: center; }
.btn {
background: white; border: none; border-radius: 10px; padding: 12px 25px;
font-size: 16px; cursor: pointer; font-family: inherit; font-weight: bold;
}
.btn-back { background: #ff6b6b; color: white; text-decoration: none; }
.status { color: white; font-size: 18px; margin: 10px 0; }
</style>
</head>
<body>
<h1>🧩 Puzzle</h1>
<div class="level-select">
<button class="level-btn" onclick="setLevel(2)" id="l2">2×2 (Leicht)</button>
<button class="level-btn active" onclick="setLevel(3)" id="l3">3×3 (Mittel)</button>
<button class="level-btn" onclick="setLevel(4)" id="l4">4×4 (Schwer)</button>
</div>
<div class="image-select" id="imageSelect"></div>
<div class="preview">
<div class="preview-img" id="previewImg">🐶</div>
<p style="color:#666;font-size:12px">So soll es aussehen!</p>
</div>
<div class="status" id="status">Klicke zwei Teile zum Tauschen!</div>
<div class="puzzle-container" id="puzzleContainer"></div>
<div class="controls">
<button class="btn" onclick="shufflePuzzle()">🔀 Mischen</button>
<button class="btn" onclick="showHint()">💡 Tipp</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<script>
const emojis = ['🐶','🐱','🐭','🐹','🐰','🦊','🐻','🐼','🐨','🐯','🦁','🐷','🐸','🐵','🐔'];
let gridSize = 3;
let selectedPiece = null;
let pieces = [];
let currentEmoji = '🐶';
let solved = false;
function init() {
// Bildauswahl
const imgSelect = document.getElementById('imageSelect');
emojis.slice(0, 8).forEach((emoji, i) => {
const btn = document.createElement('button');
btn.className = 'img-btn' + (i === 0 ? ' active' : '');
btn.textContent = emoji;
btn.onclick = () => selectImage(emoji, btn);
imgSelect.appendChild(btn);
});
createPuzzle();
}
function setLevel(n) {
gridSize = n;
document.querySelectorAll('.level-btn').forEach(b => b.classList.remove('active'));
document.getElementById('l' + n).classList.add('active');
createPuzzle();
}
function selectImage(emoji, btn) {
currentEmoji = emoji;
document.getElementById('previewImg').textContent = emoji;
document.querySelectorAll('.img-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
createPuzzle();
}
function createPuzzle() {
const container = document.getElementById('puzzleContainer');
container.innerHTML = '';
container.style.gridTemplateColumns = `repeat(${gridSize}, 80px)`;
pieces = [];
const totalPieces = gridSize * gridSize;
// Canvas für Emoji-Bild
const canvas = document.createElement('canvas');
canvas.width = 300;
canvas.height = 300;
const ctx = canvas.getContext('2d');
// Emoji zeichnen
ctx.fillStyle = '#f0f0f0';
ctx.fillRect(0, 0, 300, 300);
ctx.font = '200px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(currentEmoji, 150, 150);
const imageData = canvas.toDataURL();
// Teile erstellen
for (let i = 0; i < totalPieces; i++) {
const correctPos = i;
const piece = {
id: i,
correctPos: correctPos,
currentPos: i
};
pieces.push(piece);
}
// Rendern
renderPuzzle(imageData);
// Kurz warten dann mischen
setTimeout(shufflePuzzle, 500);
}
function renderPuzzle(imageData) {
const container = document.getElementById('puzzleContainer');
container.innerHTML = '';
const pieceSize = 80;
// Nach currentPos sortieren für Anzeige
const displayPieces = [...pieces].sort((a, b) => a.currentPos - b.currentPos);
displayPieces.forEach((piece, index) => {
const div = document.createElement('div');
div.className = 'puzzle-piece';
div.style.width = pieceSize + 'px';
div.style.height = pieceSize + 'px';
div.style.backgroundImage = `url(${imageData})`;
// Hintergrund-Position basierend auf correctPos
const row = Math.floor(piece.correctPos / gridSize);
const col = piece.correctPos % gridSize;
div.style.backgroundPosition = `-${col * pieceSize}px -${row * pieceSize}px`;
div.style.backgroundSize = `${gridSize * pieceSize}px ${gridSize * pieceSize}px`;
div.onclick = () => selectPiece(piece);
container.appendChild(div);
});
checkWin();
}
function selectPiece(piece) {
if (solved) return;
const container = document.getElementById('puzzleContainer');
const divs = container.querySelectorAll('.puzzle-piece');
// Index in display order finden
const displayIndex = pieces.findIndex(p => p.id === piece.id);
if (!selectedPiece) {
selectedPiece = piece;
divs[displayIndex].classList.add('selected');
document.getElementById('status').textContent = 'Wähle ein anderes Teil zum Tauschen!';
} else if (selectedPiece.id === piece.id) {
// Gleiches Teil = abwählen
selectedPiece = null;
divs[displayIndex].classList.remove('selected');
document.getElementById('status').textContent = 'Klicke zwei Teile zum Tauschen!';
} else {
// Tauschen
const tempPos = selectedPiece.currentPos;
selectedPiece.currentPos = piece.currentPos;
piece.currentPos = tempPos;
selectedPiece = null;
// Neu rendern
const canvas = document.createElement('canvas');
canvas.width = 300; canvas.height = 300;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#f0f0f0';
ctx.fillRect(0, 0, 300, 300);
ctx.font = '200px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(currentEmoji, 150, 150);
renderPuzzle(canvas.toDataURL());
}
}
function shufflePuzzle() {
if (solved) solved = false;
// Fisher-Yates Shuffle für currentPos
for (let i = pieces.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
const temp = pieces[i].currentPos;
pieces[i].currentPos = pieces[j].currentPos;
pieces[j].currentPos = temp;
}
// Neu rendern
const canvas = document.createElement('canvas');
canvas.width = 300; canvas.height = 300;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#f0f0f0';
ctx.fillRect(0, 0, 300, 300);
ctx.font = '200px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(currentEmoji, 150, 150);
renderPuzzle(canvas.toDataURL());
document.getElementById('status').textContent = 'Klicke zwei Teile zum Tauschen!';
}
function checkWin() {
const isSolved = pieces.every(p => p.currentPos === p.correctPos);
if (isSolved && !solved) {
solved = true;
document.getElementById('status').textContent = '🎉 Super! Puzzle gelöst!';
// Alle Teile grün blinken
const divs = document.querySelectorAll('.puzzle-piece');
divs.forEach(div => {
div.style.boxShadow = '0 0 20px #4ade80';
});
if ('speechSynthesis' in window) {
const utterance = new SpeechSynthesisUtterance('Super! Du hast das Puzzle gelöst!');
utterance.lang = 'de-DE';
speechSynthesis.speak(utterance);
}
}
}
function showHint() {
// Zeige für 2 Sekunden die richtige Position an
const container = document.getElementById('puzzleContainer');
const divs = container.querySelectorAll('.puzzle-piece');
divs.forEach((div, index) => {
const piece = pieces.find(p => p.currentPos === index);
if (piece && piece.correctPos === index) {
div.style.boxShadow = '0 0 10px #4ade80';
}
});
setTimeout(() => {
divs.forEach(div => {
div.style.boxShadow = '';
});
}, 2000);
document.getElementById('status').textContent = '💡 Grün markierte Teile sind richtig!';
}
// Init
init();
</script>
</body>
</html>
+231
View File
@@ -0,0 +1,231 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Reaktionstest - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 20px;
}
h1 { color: white; margin-bottom: 15px; font-size: 26px; }
.game-area {
width: 300px; height: 300px; border-radius: 20px;
display: flex; justify-content: center; align-items: center;
cursor: pointer; transition: all 0.1s; margin: 20px 0;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
}
.game-area.wait { background: #ff6b6b; }
.game-area.ready { background: #4ade80; animation: pulse 0.5s infinite; }
.game-area.too-soon { background: #feca57; }
.game-area.start { background: #3b82f6; }
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
.game-text {
color: white; font-size: 28px; font-weight: bold; text-align: center;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.stats {
display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px;
margin: 20px 0; max-width: 300px;
}
.stat-box {
background: white; border-radius: 15px; padding: 15px; text-align: center;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.stat-label { color: #666; font-size: 12px; }
.stat-value { color: #333; font-size: 24px; font-weight: bold; }
.stat-value.best { color: #4ade80; }
.controls { display: flex; gap: 15px; margin-top: 20px; }
.btn {
background: white; border: none; border-radius: 10px; padding: 12px 25px;
font-size: 16px; cursor: pointer; font-family: inherit; font-weight: bold;
}
.btn-back { background: #ff6b6b; color: white; text-decoration: none; }
.instructions { color: white; text-align: center; max-width: 400px; margin: 10px 0; }
.reaction-list { background: white; border-radius: 15px; padding: 15px; margin: 10px 0; }
.reaction-item { display: flex; justify-content: space-between; padding: 5px 0; border-bottom: 1px solid #eee; }
</style>
</head>
<body>
<h1>⚡ Reaktionstest</h1>
<p class="instructions">
Warte bis es GRÜN wird, dann so schnell wie möglich klicken!
<br>Aber nicht zu früh...
</p>
<div class="stats">
<div class="stat-box">
<div class="stat-label">Letzte Zeit</div>
<div class="stat-value" id="lastTime">-</div>
</div>
<div class="stat-box">
<div class="stat-label">Bestzeit</div>
<div class="stat-value best" id="bestTime">-</div>
</div>
</div>
<div class="game-area start" id="gameArea" onclick="handleClick()">
<div class="game-text" id="gameText">
Klicken zum
<br>Starten
</div>
</div>
<div class="reaction-list" id="reactionList" style="display:none">
<p style="font-weight:bold;margin-bottom:10px">Letzte Versuche:</p>
<div id="reactionHistory"></div>
</div>
<div class="controls">
<button class="btn" onclick="resetBest()">🗑️ Reset Bestzeit</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<script>
// Zustände: start, waiting, ready, too_soon, result
let state = 'start';
let startTime = 0;
let waitTimer = null;
let bestTime = localStorage.getItem('reaktion_bestzeit');
let lastTime = null;
let reactionHistory = [];
const gameArea = document.getElementById('gameArea');
const gameText = document.getElementById('gameText');
// Bestzeit anzeigen
if (bestTime) {
document.getElementById('bestTime').textContent = bestTime + ' ms';
}
function handleClick() {
if (state === 'start') {
// Spiel starten
startWaiting();
} else if (state === 'waiting') {
// Zu früh!
tooSoon();
} else if (state === 'ready') {
// Reaktion messen
measureReaction();
} else if (state === 'too_soon' || state === 'result') {
// Neu starten
resetToStart();
}
}
function startWaiting() {
state = 'waiting';
startTime = 0;
gameArea.className = 'game-area wait';
gameText.innerHTML = 'Warte auf<br>GRÜN...';
// Zufällige Zeit 1-4 Sekunden
const delay = 1000 + Math.random() * 3000;
waitTimer = setTimeout(() => {
if (state === 'waiting') {
state = 'ready';
gameArea.className = 'game-area ready';
gameText.innerHTML = 'JETZT!<br>🖱️ Klick!';
startTime = Date.now();
}
}, delay);
}
function tooSoon() {
clearTimeout(waitTimer);
state = 'too_soon';
gameArea.className = 'game-area too-soon';
gameText.innerHTML = 'Zu früh!<br>😅<br><small>Nochmal klicken</small>';
}
function measureReaction() {
const endTime = Date.now();
const reactionTime = endTime - startTime;
state = 'result';
// Bewertung
let message = '';
let emoji = '';
if (reactionTime < 200) {
message = 'Unglaublich!';
emoji = '🚀';
} else if (reactionTime < 300) {
message = 'Super schnell!';
emoji = '⚡';
} else if (reactionTime < 400) {
message = 'Sehr gut!';
emoji = '👍';
} else if (reactionTime < 600) {
message = 'Gut!';
emoji = '👌';
} else {
message = 'Übe mehr!';
emoji = '💪';
}
gameArea.className = 'game-area wait';
gameText.innerHTML = reactionTime + ' ms<br>' + emoji + '<br><small>' + message + '</small>';
// Speichern
lastTime = reactionTime;
document.getElementById('lastTime').textContent = reactionTime + ' ms';
if (!bestTime || reactionTime < bestTime) {
bestTime = reactionTime;
localStorage.setItem('reaktion_bestzeit', bestTime);
document.getElementById('bestTime').textContent = bestTime + ' ms';
}
// Historie
reactionHistory.unshift({ time: reactionTime });
if (reactionHistory.length > 5) reactionHistory.pop();
updateHistory();
// TTS
if ('speechSynthesis' in window) {
const utterance = new SpeechSynthesisUtterance(reactionTime + ' Millisekunden. ' + message);
utterance.lang = 'de-DE';
utterance.rate = 0.9;
speechSynthesis.speak(utterance);
}
}
function resetToStart() {
state = 'start';
startTime = 0;
gameArea.className = 'game-area start';
gameText.innerHTML = 'Klicken zum<br>Starten';
}
function resetBest() {
localStorage.removeItem('reaktion_bestzeit');
bestTime = null;
document.getElementById('bestTime').textContent = '-';
}
function updateHistory() {
const list = document.getElementById('reactionHistory');
document.getElementById('reactionList').style.display = reactionHistory.length > 0 ? 'block' : 'none';
list.innerHTML = reactionHistory.map((r, i) => {
return '<div class="reaction-item"><span>' + (i+1) + '. Versuch</span><span>' + r.time + ' ms</span></div>';
}).join('');
}
// Touch für Mobile
gameArea.addEventListener('touchstart', function(e) {
e.preventDefault();
handleClick();
});
</script>
</body>
</html>
+588
View File
@@ -0,0 +1,588 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Schach - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #2d3748 0%, #1a202c 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 10px;
}
h1 { color: white; margin-bottom: 10px; font-size: 24px; }
.board-container {
background: #5D4037; padding: 10px; border-radius: 10px;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
}
.chess-board {
display: grid; grid-template-columns: repeat(8, 45px); gap: 0;
border: 3px solid #3E2723;
}
.square {
width: 45px; height: 45px; display: flex;
align-items: center; justify-content: center; position: relative;
cursor: pointer; transition: all 0.15s;
}
.square.light { background: #F0D9B5; }
.square.dark { background: #B58863; }
.square.selected { background: #7B61FF !important; }
.square.valid-move::after {
content: ''; position: absolute; width: 12px; height: 12px;
background: rgba(123, 97, 255, 0.8); border-radius: 50%;
}
.square.valid-capture { background: #FF6B6B !important; }
.square.last-move { box-shadow: inset 0 0 0 3px #FFD93D; }
.square.check {
background: #FF4444 !important;
animation: pulse-check 0.5s infinite;
}
@keyframes pulse-check {
0%, 100% { box-shadow: inset 0 0 0 rgba(255,0,0,0); }
50% { box-shadow: inset 0 0 15px rgba(255,0,0,0.8); }
}
.piece {
font-size: 32px; cursor: grab; transition: transform 0.15s;
text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
}
.piece:hover { transform: scale(1.15); }
.piece.white {
filter: drop-shadow(0 0 3px #fff) drop-shadow(0 0 5px #FFD700);
}
.piece.black {
filter: drop-shadow(0 0 3px #000);
}
.coordinates {
display: flex; justify-content: space-around; width: 360px;
color: white; font-size: 11px; margin-top: 3px;
}
.status {
background: rgba(255,255,255,0.15); padding: 12px 25px; border-radius: 25px;
color: white; margin: 12px 0; text-align: center; font-size: 15px; min-height: 45px;
}
.turn-white { color: #FFD700; font-weight: bold; }
.turn-black { color: #AAA; font-weight: bold; }
.mode-select { margin: 10px 0; }
.mode-btn {
background: rgba(255,255,255,0.2); border: 2px solid white; color: white;
padding: 8px 20px; margin: 0 5px; border-radius: 20px; cursor: pointer;
}
.mode-btn.active { background: white; color: #2d3748; }
.hint-btn {
background: #F59E0B; color: white; padding: 10px 20px;
border: none; border-radius: 10px; cursor: pointer;
font-family: inherit; font-weight: bold; font-size: 14px;
}
.promotion-modal {
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: white; padding: 25px; border-radius: 15px; display: none; z-index: 100;
}
.promotion-piece { font-size: 50px; margin: 0 10px; cursor: pointer; }
.promotion-piece:hover { transform: scale(1.2); }
.controls { display: flex; gap: 10px; margin-top: 15px; flex-wrap: wrap; justify-content: center; }
.btn {
padding: 10px 20px; border: none; border-radius: 10px; cursor: pointer;
font-family: inherit; font-weight: bold; font-size: 14px;
}
.btn-new { background: #4ade80; color: white; }
.btn-back { background: #ff6b6b; color: white; text-decoration: none; }
.exercise-btn {
background: rgba(255,255,255,0.2); border: 2px solid white; color: white;
padding: 8px 15px; margin: 5px; border-radius: 10px; cursor: pointer;
}
.exercise-btn:hover { background: rgba(255,255,255,0.4); }
.game-over {
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: white; padding: 30px; border-radius: 20px; display: none; z-index: 100;
text-align: center;
}
</style>
</head>
<body>
<h1>♟️ Schach</h1>
<div class="mode-select">
<button class="mode-btn active" onclick="setMode('free')">Freies Spiel</button>
<button class="mode-btn" onclick="setMode('mate1')">Matt in 1</button>
<button class="mode-btn" onclick="setMode('fork')">Gabel</button>
</div>
<div class="status" id="status"><span class="turn-white">Weiß</span> ist am Zug</div>
<div class="board-container">
<div class="chess-board" id="board"></div>
</div>
<div class="coordinates">
<span>a</span><span>b</span><span>c</span><span>d</span>
<span>e</span><span>f</span><span>g</span><span>h</span>
</div>
<div class="controls">
<button class="hint-btn" onclick="showHint()">💡 Tipp</button>
<button class="btn btn-new" onclick="newGame()">🔄 Neues Spiel</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<div class="promotion-modal" id="promotionModal">
<h3>Wähle Figur:</h3>
<div id="promotionOptions"></div>
</div>
<div class="game-over" id="gameOver">
<h2 id="winnerText">Schachmatt!</h2>
<button class="btn btn-new" onclick="newGame()">🔄 Nochmal</button>
</div>
<script>
const PIECES = {
'K': '♔', 'Q': '♕', 'R': '♖', 'B': '♗', 'N': '♘', 'P': '♙',
'k': '♚', 'q': '♛', 'r': '♜', 'b': '♝', 'n': '♞', 'p': '♟'
};
let board = [];
let selected = null;
let validMoves = [];
let currentPlayer = 'white';
let lastMove = null;
let waitingPromotion = null;
let currentExercise = null;
const START_POS = [
'rnbqkbnr',
'pppppppp',
' ',
' ',
' ',
' ',
'PPPPPPPP',
'RNBQKBNR'
];
const EXERCISES = {
mate1: [
{
pos: [
' ',
' ',
' ',
' k ',
' ',
' Q ',
' ',
' K'
],
solution: { from: {r: 5, c: 5}, to: {r: 3, c: 5} }, // Qf6-f7 Matt
hint: 'Setze die Dame neben den König auf f7!'
},
{
pos: [
' ',
' ',
' ',
' k ',
' ',
' ',
'R ',
' K'
],
solution: { from: {r: 6, c: 0}, to: {r: 0, c: 0} },
hint: 'Der Turm schließt den König auf der a-Linie ein!'
},
{
pos: [
'k ',
' ',
' ',
' ',
' ',
' ',
'R R',
' K'
],
solution: { from: {r: 6, c: 0}, to: {r: 0, c: 0} },
hint: 'Ein Turm geht auf die 8. Reihe für Matt!'
}
],
fork: [
{
pos: [
' ',
' q ',
' ',
' ',
' k ',
' ',
' N',
' K'
],
solution: { from: {r: 6, c: 7}, to: {r: 4, c: 5} },
hint: 'Der Springer greift König und Dame gleichzeitig an!'
}
]
};
function initBoard() {
const boardEl = document.getElementById('board');
boardEl.innerHTML = '';
board = [];
for (let row = 0; row < 8; row++) {
board[row] = [];
for (let col = 0; col < 8; col++) {
const cell = document.createElement('div');
cell.className = 'square ' + ((row + col) % 2 === 0 ? 'light' : 'dark');
cell.dataset.r = row;
cell.dataset.c = col;
cell.onclick = () => clickSquare(row, col);
const piece = START_POS[row][col];
if (piece !== ' ') {
setPiece(cell, piece);
board[row][col] = piece;
}
boardEl.appendChild(cell);
}
}
updateStatus();
}
function setPiece(cell, piece) {
const span = document.createElement('span');
span.className = 'piece ' + (piece === piece.toUpperCase() ? 'white' : 'black');
span.textContent = PIECES[piece];
cell.innerHTML = '';
cell.appendChild(span);
}
function clickSquare(row, col) {
if (waitingPromotion) return;
const piece = board[row][col];
const isOwn = piece &&
((currentPlayer === 'white' && piece === piece.toUpperCase()) ||
(currentPlayer === 'black' && piece === piece.toLowerCase()));
if (!selected && isOwn) {
selected = { r: row, c: col };
validMoves = getMoves(row, col, piece);
highlightSquare(row, col, 'selected');
highlightMoves();
} else if (selected) {
const move = validMoves.find(m => m.r === row && m.c === col);
if (move) {
makeMove(selected.r, selected.c, row, col);
}
clearHighlights();
selected = null;
validMoves = [];
}
}
function getMoves(r, c, piece) {
const moves = [];
const isWhite = piece === piece.toUpperCase();
const p = piece.toLowerCase();
if (p === 'p') {
const dir = isWhite ? -1 : 1;
const startRow = isWhite ? 6 : 1;
if (isEmpty(r + dir, c)) {
moves.push({ r: r + dir, c });
if (r === startRow && isEmpty(r + 2*dir, c)) {
moves.push({ r: r + 2*dir, c });
}
}
[-1, 1].forEach(dc => {
if (canCapture(r + dir, c + dc, isWhite)) {
moves.push({ r: r + dir, c: c + dc });
}
});
}
else if (p === 'n') {
[[-2,-1],[-2,1],[-1,-2],[-1,2],[1,-2],[1,2],[2,-1],[2,1]].forEach(([dr, dc]) => {
if (canMove(r + dr, c + dc, isWhite)) moves.push({ r: r + dr, c: c + dc });
});
}
else if (p === 'k') {
[[-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1]].forEach(([dr, dc]) => {
if (canMove(r + dr, c + dc, isWhite)) moves.push({ r: r + dr, c: c + dc });
});
}
else if (p === 'r') {
addLineMoves(moves, r, c, [[0,1],[0,-1],[1,0],[-1,0]], isWhite);
}
else if (p === 'b') {
addLineMoves(moves, r, c, [[1,1],[1,-1],[-1,1],[-1,-1]], isWhite);
}
else if (p === 'q') {
addLineMoves(moves, r, c, [[0,1],[0,-1],[1,0],[-1,0],[1,1],[1,-1],[-1,1],[-1,-1]], isWhite);
}
return moves;
}
function addLineMoves(moves, r, c, dirs, isWhite) {
dirs.forEach(([dr, dc]) => {
for (let i = 1; i < 8; i++) {
const nr = r + dr * i, nc = c + dc * i;
if (!inBounds(nr, nc)) break;
if (isEmpty(nr, nc)) {
moves.push({ r: nr, c: nc });
} else if (canCapture(nr, nc, isWhite)) {
moves.push({ r: nr, c: nc });
break;
} else break;
}
});
}
function inBounds(r, c) { return r >= 0 && r < 8 && c >= 0 && c < 8; }
function isEmpty(r, c) { return inBounds(r, c) && !board[r][c]; }
function canCapture(r, c, isWhite) {
if (!inBounds(r, c)) return false;
const target = board[r][c];
return target && (isWhite ? target === target.toLowerCase() : target === target.toUpperCase());
}
function canMove(r, c, isWhite) {
return inBounds(r, c) && (isEmpty(r, c) || canCapture(r, c, isWhite));
}
function makeMove(fr, fc, tr, tc) {
const piece = board[fr][fc];
const captured = board[tr][tc];
board[tr][tc] = piece;
board[fr][fc] = null;
// König geschlagen = Spielende
if (captured === 'K' || captured === 'k') {
endGame(captured === 'K' ? 'Schwarz' : 'Weiß');
return;
}
// Bauern-Umwandlung
if (piece.toLowerCase() === 'p' && (tr === 0 || tr === 7)) {
showPromotion(tr, tc, piece === piece.toUpperCase());
return;
}
finishMove(fr, fc, tr, tc, piece);
}
function showPromotion(r, c, isWhite) {
waitingPromotion = { r, c, isWhite };
const modal = document.getElementById('promotionModal');
const opts = document.getElementById('promotionOptions');
opts.innerHTML = '';
const pieces = isWhite ? ['Q','R','B','N'] : ['q','r','b','n'];
pieces.forEach(p => {
const btn = document.createElement('span');
btn.className = 'promotion-piece';
btn.textContent = PIECES[p];
btn.onclick = () => {
board[r][c] = p;
modal.style.display = 'none';
waitingPromotion = null;
finishMove(null, null, null, null, p);
};
opts.appendChild(btn);
});
modal.style.display = 'block';
}
function finishMove(fr, fc, tr, tc, piece) {
lastMove = { fr, fc, tr, tc };
updateBoard();
// Prüfe Übungslösung
if (currentExercise) {
const sol = currentExercise.solution;
if (lastMove.fr === sol.from.r && lastMove.fc === sol.from.c &&
lastMove.tr === sol.to.r && lastMove.tc === sol.to.c) {
setTimeout(() => {
alert('🎉 Richtig! ' + currentExercise.hint);
currentExercise = null;
}, 300);
}
}
switchPlayer();
}
function switchPlayer() {
currentPlayer = currentPlayer === 'white' ? 'black' : 'white';
updateStatus();
// Prüfe Schach
if (isInCheck(currentPlayer)) {
highlightKing(currentPlayer);
if (isCheckmate(currentPlayer)) {
endGame(currentPlayer === 'white' ? 'Schwarz' : 'Weiß');
}
}
}
function isInCheck(color) {
const king = color === 'white' ? 'K' : 'k';
let kr, kc;
for (let r = 0; r < 8; r++) {
for (let c = 0; c < 8; c++) {
if (board[r][c] === king) { kr = r; kc = c; }
}
}
for (let r = 0; r < 8; r++) {
for (let c = 0; c < 8; c++) {
const p = board[r][c];
if (p && (color === 'white' ? p === p.toLowerCase() : p === p.toUpperCase())) {
const moves = getMoves(r, c, p);
if (moves.some(m => m.r === kr && m.c === kc)) return true;
}
}
}
return false;
}
function isCheckmate(color) {
// Einfach: Keine legalen Züge + Schach
if (!isInCheck(color)) return false;
for (let r = 0; r < 8; r++) {
for (let c = 0; c < 8; c++) {
const p = board[r][c];
if (p && (color === 'white' ? p === p.toUpperCase() : p === p.toLowerCase())) {
if (getMoves(r, c, p).length > 0) return false;
}
}
}
return true;
}
function highlightKing(color) {
const king = color === 'white' ? 'K' : 'k';
for (let r = 0; r < 8; r++) {
for (let c = 0; c < 8; c++) {
if (board[r][c] === king) {
document.querySelector(`[data-r="${r}"][data-c="${c}"]`).classList.add('check');
}
}
}
}
function updateBoard() {
for (let r = 0; r < 8; r++) {
for (let c = 0; c < 8; c++) {
const cell = document.querySelector(`[data-r="${r}"][data-c="${c}"]`);
const p = board[r][c];
cell.innerHTML = '';
cell.classList.remove('last-move', 'check');
if (p) setPiece(cell, p);
if (lastMove && ((r === lastMove.fr && c === lastMove.fc) || (r === lastMove.tr && c === lastMove.tc))) {
cell.classList.add('last-move');
}
}
}
}
function updateStatus() {
const status = document.getElementById('status');
const cls = currentPlayer === 'white' ? 'turn-white' : 'turn-black';
status.innerHTML = `<span class="${cls}">${currentPlayer === 'white' ? 'Weiß' : 'Schwarz'}</span> ist am Zug`;
}
function highlightSquare(r, c, cls) {
document.querySelector(`[data-r="${r}"][data-c="${c}"]`).classList.add(cls);
}
function highlightMoves() {
validMoves.forEach(m => {
const cell = document.querySelector(`[data-r="${m.r}"][data-c="${m.c}"]`);
if (board[m.r][m.c]) cell.classList.add('valid-capture');
else cell.classList.add('valid-move');
});
}
function clearHighlights() {
document.querySelectorAll('.square').forEach(s => {
s.classList.remove('selected', 'valid-move', 'valid-capture');
});
}
function setMode(mode) {
document.querySelectorAll('.mode-btn').forEach(b => b.classList.remove('active'));
event.target.classList.add('active');
if (mode === 'free') {
currentExercise = null;
newGame();
} else if (EXERCISES[mode]) {
const ex = EXERCISES[mode][Math.floor(Math.random() * EXERCISES[mode].length)];
currentExercise = ex;
loadPosition(ex.pos);
}
}
function loadPosition(pos) {
board = [];
for (let r = 0; r < 8; r++) {
board[r] = [];
for (let c = 0; c < 8; c++) {
board[r][c] = pos[r][c] === ' ' ? null : pos[r][c];
}
}
currentPlayer = 'white';
updateBoard();
updateStatus();
}
function showHint() {
if (!currentExercise) {
alert('💡 Tipps sind nur im Übungsmodus verfügbar!');
return;
}
alert('💡 ' + currentExercise.hint);
}
function endGame(winner) {
document.getElementById('winnerText').textContent = `🎉 ${winner} gewinnt!`;
document.getElementById('gameOver').style.display = 'block';
}
function newGame() {
document.getElementById('gameOver').style.display = 'none';
document.getElementById('promotionModal').style.display = 'none';
waitingPromotion = null;
selected = null;
validMoves = [];
lastMove = null;
currentPlayer = 'white';
currentExercise = null;
initBoard();
}
// Start
initBoard();
</script>
</body>
</html>
+210
View File
@@ -0,0 +1,210 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Simon Says - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 20px;
}
h1 { color: white; margin-bottom: 10px; font-size: 28px; }
.score { color: #4ade80; font-size: 20px; margin-bottom: 20px; }
.game-board {
display: grid; grid-template-columns: 1fr 1fr; gap: 10px;
width: 300px; height: 300px; margin: 20px 0;
}
.color-btn {
border: none; border-radius: 20px; cursor: pointer;
opacity: 0.7; transition: all 0.1s;
}
.color-btn.active { opacity: 1; transform: scale(0.95); box-shadow: 0 0 30px currentColor; }
.color-btn:active { transform: scale(0.9); }
.green { background: #22c55e; }
.red { background: #ef4444; }
.yellow { background: #eab308; }
.blue { background: #3b82f6; }
.center-btn {
position: absolute; width: 80px; height: 80px;
background: #1a1a2e; border: 4px solid white; border-radius: 50%;
color: white; font-size: 14px; cursor: pointer;
display: flex; align-items: center; justify-content: center;
}
.game-area { position: relative; }
.status { color: white; font-size: 24px; margin: 20px 0; min-height: 40px; }
.controls { display: flex; gap: 15px; margin-top: 20px; }
.btn {
background: white; border: none; border-radius: 10px; padding: 15px 30px;
font-size: 18px; cursor: pointer; font-family: inherit; font-weight: bold;
}
.btn-back { background: #ff6b6b; color: white; text-decoration: none; }
.btn-start { background: #4ade80; color: white; }
.highscore { color: #feca57; margin-top: 10px; }
</style>
</head>
<body>
<h1>🎵 Simon Says</h1>
<div class="score">Level: <span id="level">0</span></div>
<div class="highscore">Rekord: <span id="highscore">0</span></div>
<div class="game-area">
<div class="game-board">
<button class="color-btn green" data-color="0" onclick="playerClick(0)"></button>
<button class="color-btn red" data-color="1" onclick="playerClick(1)"></button>
<button class="color-btn yellow" data-color="2" onclick="playerClick(2)"></button>
<button class="color-btn blue" data-color="3" onclick="playerClick(3)"></button>
</div>
</div>
<div class="status" id="status">Drücke Start!</div>
<div class="controls">
<button class="btn btn-start" id="startBtn" onclick="startGame()">▶️ Start</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<script>
const colors = [
{ name: 'green', sound: 523.25 }, // C5
{ name: 'red', sound: 659.25 }, // E5
{ name: 'yellow', sound: 783.99 }, // G5
{ name: 'blue', sound: 987.77 } // B5
];
let sequence = [];
let playerSequence = [];
let level = 0;
let highscore = localStorage.getItem('simon_highscore') || 0;
let gameActive = false;
let audioCtx = null;
document.getElementById('highscore').textContent = highscore;
function initAudio() {
if (!audioCtx) {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
}
}
function playTone(freq, duration = 300) {
if (!audioCtx) return;
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.frequency.value = freq;
gain.gain.setValueAtTime(0.3, audioCtx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + duration / 1000);
osc.start();
osc.stop(audioCtx.currentTime + duration / 1000);
}
function flashColor(colorIndex, duration = 400) {
const btn = document.querySelector(`[data-color="${colorIndex}"]`);
btn.classList.add('active');
playTone(colors[colorIndex].sound);
setTimeout(() => btn.classList.remove('active'), duration);
}
function startGame() {
initAudio();
sequence = [];
playerSequence = [];
level = 0;
gameActive = true;
document.getElementById('level').textContent = level;
document.getElementById('status').textContent = 'Schau genau hin!';
document.getElementById('startBtn').textContent = '🔄 Neustart';
setTimeout(nextRound, 1000);
}
function nextRound() {
playerSequence = [];
level++;
document.getElementById('level').textContent = level;
document.getElementById('status').textContent = `Level ${level} - Beobachte!`;
sequence.push(Math.floor(Math.random() * 4));
let i = 0;
const playSequence = () => {
if (i < sequence.length) {
flashColor(sequence[i], 500);
i++;
setTimeout(playSequence, 700);
} else {
document.getElementById('status').textContent = 'Dein Zug!';
}
};
setTimeout(playSequence, 500);
}
function playerClick(color) {
if (!gameActive) {
flashColor(color, 200);
return;
}
flashColor(color, 300);
playerSequence.push(color);
const currentIndex = playerSequence.length - 1;
if (playerSequence[currentIndex] !== sequence[currentIndex]) {
// Falsch
gameOver();
return;
}
if (playerSequence.length === sequence.length) {
// Richtig!
document.getElementById('status').textContent = '🎉 Super!';
setTimeout(nextRound, 1000);
}
}
function gameOver() {
gameActive = false;
document.getElementById('status').textContent = `💥 Game Over! Level ${level}`;
// Fehler-Ton
if (audioCtx) {
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.frequency.value = 150;
gain.gain.setValueAtTime(0.5, audioCtx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.5);
osc.start();
osc.stop(audioCtx.currentTime + 0.5);
}
if (level > highscore) {
highscore = level;
localStorage.setItem('simon_highscore', highscore);
document.getElementById('highscore').textContent = highscore;
document.getElementById('status').textContent = `🏆 Neuer Rekord! Level ${level}`;
}
}
// Tastatur-Steuerung
document.addEventListener('keydown', (e) => {
switch(e.key) {
case 'ArrowUp': playerClick(0); break;
case 'ArrowRight': playerClick(1); break;
case 'ArrowDown': playerClick(2); break;
case 'ArrowLeft': playerClick(3); break;
case ' ': startGame(); break;
}
});
</script>
</body>
</html>
+302
View File
@@ -0,0 +1,302 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Snake - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; touch-action: manipulation; }
body {
background: linear-gradient(135deg, #1a472a 0%, #2d5016 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 5px;
}
h1 { color: #4ade80; margin: 5px 0; font-size: 22px; }
.score-board { color: white; font-size: 16px; margin-bottom: 10px; text-align: center; }
#gameCanvas {
background: #0f291e; border: 3px solid #4ade80; border-radius: 8px;
box-shadow: 0 0 15px rgba(74, 222, 128, 0.3); display: block; margin: 0 auto;
}
.main-area { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.controls {
display: flex; gap: 10px; margin: 10px 0; flex-wrap: wrap; justify-content: center;
}
.btn {
background: linear-gradient(135deg, #4ade80, #22c55e); border: none; border-radius: 8px;
padding: 12px 20px; font-size: 16px; cursor: pointer; font-family: inherit; font-weight: bold;
color: white; box-shadow: 0 3px 8px rgba(0,0,0,0.3);
}
.btn:active { transform: scale(0.95); }
.btn-back { background: #ff6b6b; text-decoration: none; }
/* Big Touch Controls */
.touch-controls-area {
display: flex; flex-direction: column; align-items: center;
gap: 5px; margin-top: 10px;
}
.d-pad {
display: grid;
grid-template-columns: 80px 80px 80px;
gap: 8px;
}
.d-btn {
width: 80px; height: 80px;
background: rgba(255,255,255,0.15);
border: 3px solid #4ade80;
border-radius: 15px;
font-size: 36px;
color: #4ade80;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
touch-action: manipulation;
}
.d-btn:active {
background: rgba(74, 222, 128, 0.4);
transform: scale(0.95);
}
.d-btn.center {
background: rgba(74, 222, 128, 0.25);
font-size: 24px;
}
@media (min-width: 700px) {
.main-area { flex-direction: row; align-items: flex-start; }
}
</style>
</head>
<body>
<h1>🐍 Snake</h1>
<div class="score-board">Punkte: <span id="score">0</span> | Highscore: <span id="highscore">0</span></div>
<div class="main-area">
<canvas id="gameCanvas" width="360" height="360"></canvas>
<div class="controls-panel">
<div class="controls">
<button class="btn" id="startBtn" onclick="startGame()">▶️ Start</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<!-- Touch Controls -->
<div class="touch-controls-area">
<div class="d-pad">
<div></div>
<button class="d-btn" ontouchstart="changeDirection('up'); return false;" onclick="changeDirection('up')"></button>
<div></div>
<button class="d-btn" ontouchstart="changeDirection('left'); return false;" onclick="changeDirection('left')"></button>
<button class="d-btn center" onclick="startGame()"></button>
<button class="d-btn" ontouchstart="changeDirection('right'); return false;" onclick="changeDirection('right')"></button>
<div></div>
<button class="d-btn" ontouchstart="changeDirection('down'); return false;" onclick="changeDirection('down')"></button>
<div></div>
</div>
</div>
</div>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Game variables
const gridSize = 18;
const tileCount = canvas.width / gridSize;
let snake = [];
let food = {};
let direction = 'right';
let nextDirection = 'right';
let score = 0;
let highscore = localStorage.getItem('snake_highscore') || 0;
let gameRunning = false;
let gameLoopId;
document.getElementById('highscore').textContent = highscore;
function initGame() {
snake = [
{x: 5, y: 10},
{x: 4, y: 10},
{x: 3, y: 10}
];
direction = 'right';
nextDirection = 'right';
score = 0;
document.getElementById('score').textContent = score;
spawnFood();
}
function spawnFood() {
food = {
x: Math.floor(Math.random() * tileCount),
y: Math.floor(Math.random() * tileCount)
};
for (let segment of snake) {
if (segment.x === food.x && segment.y === food.y) {
spawnFood();
return;
}
}
}
function changeDirection(newDir) {
if (!gameRunning) {
startGame();
return;
}
const opposites = {up: 'down', down: 'up', left: 'right', right: 'left'};
if (opposites[newDir] !== direction) {
nextDirection = newDir;
}
}
function update() {
if (!gameRunning) return;
direction = nextDirection;
const head = {...snake[0]};
switch(direction) {
case 'up': head.y--; break;
case 'down': head.y++; break;
case 'left': head.x--; break;
case 'right': head.x++; break;
}
// Wall collision
if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount) {
gameOver();
return;
}
// Self collision
for (let segment of snake) {
if (head.x === segment.x && head.y === segment.y) {
gameOver();
return;
}
}
snake.unshift(head);
// Check food
if (head.x === food.x && head.y === food.y) {
score += 10;
document.getElementById('score').textContent = score;
spawnFood();
} else {
snake.pop();
}
}
function draw() {
// Background
ctx.fillStyle = '#0f291e';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Food
ctx.fillStyle = '#ff6b6b';
ctx.beginPath();
ctx.arc(
food.x * gridSize + gridSize/2,
food.y * gridSize + gridSize/2,
gridSize/2 - 2,
0, Math.PI * 2
);
ctx.fill();
// Snake
snake.forEach((segment, index) => {
ctx.fillStyle = index === 0 ? '#4ade80' : '#22c55e';
ctx.fillRect(
segment.x * gridSize + 1,
segment.y * gridSize + 1,
gridSize - 2,
gridSize - 2
);
// Eyes for head
if (index === 0) {
ctx.fillStyle = '#0f291e';
let eyeX1 = segment.x * gridSize + 4;
let eyeY1 = segment.y * gridSize + 5;
let eyeX2 = segment.x * gridSize + 11;
let eyeY2 = segment.y * gridSize + 5;
if (direction === 'up') { eyeY1 -= 2; eyeY2 -= 2; }
if (direction === 'down') { eyeY1 += 2; eyeY2 += 2; }
if (direction === 'left') { eyeX1 -= 2; eyeX2 -= 2; }
if (direction === 'right') { eyeX1 += 2; eyeX2 += 2; }
ctx.fillRect(eyeX1, eyeY1, 3, 3);
ctx.fillRect(eyeX2, eyeY2, 3, 3);
}
});
}
function gameLoop() {
update();
draw();
gameLoopId = setTimeout(gameLoop, 100);
}
function startGame() {
if (gameRunning) {
// Neustart
clearTimeout(gameLoopId);
}
initGame();
gameRunning = true;
document.getElementById('startBtn').textContent = '🔄 Neustart';
gameLoop();
}
function gameOver() {
gameRunning = false;
clearTimeout(gameLoopId);
if (score > highscore) {
highscore = score;
localStorage.setItem('snake_highscore', highscore);
document.getElementById('highscore').textContent = highscore;
}
// Draw Game Over
ctx.fillStyle = 'rgba(0,0,0,0.7)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'white';
ctx.font = 'bold 30px Comic Sans MS';
ctx.textAlign = 'center';
ctx.fillText('Game Over!', canvas.width/2, canvas.height/2 - 20);
ctx.font = '20px Comic Sans MS';
ctx.fillText('Punkte: ' + score, canvas.width/2, canvas.height/2 + 20);
document.getElementById('startBtn').textContent = '▶️ Nochmal';
}
// Keyboard controls
document.addEventListener('keydown', (e) => {
if (!gameRunning) return;
switch(e.key) {
case 'ArrowUp': case 'w': e.preventDefault(); changeDirection('up'); break;
case 'ArrowDown': case 's': e.preventDefault(); changeDirection('down'); break;
case 'ArrowLeft': case 'a': e.preventDefault(); changeDirection('left'); break;
case 'ArrowRight': case 'd': e.preventDefault(); changeDirection('right'); break;
}
});
// Initial draw
initGame();
draw();
</script>
</body>
</html>
+207
View File
@@ -0,0 +1,207 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Tennis - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; overflow: hidden;
}
h1 { color: white; margin: 10px 0; font-size: 24px; }
.score { color: white; font-size: 20px; margin-bottom: 10px; }
#gameCanvas {
background: #4ade80; border: 4px solid white; border-radius: 10px;
box-shadow: 0 0 20px rgba(0,0,0,0.3);
}
.controls {
display: flex; gap: 20px; margin-top: 15px; flex-wrap: wrap; justify-content: center;
}
.btn {
background: white; border: none; border-radius: 10px; padding: 15px 30px;
font-size: 20px; cursor: pointer; box-shadow: 0 4px 10px rgba(0,0,0,0.2);
font-family: inherit; font-weight: bold;
}
.btn:active { transform: scale(0.95); }
.btn-back { background: #ff6b6b; color: white; text-decoration: none; display: inline-block; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.instructions { color: white; margin-top: 10px; text-align: center; opacity: 0.9; }
</style>
</head>
<body>
<h1>🎾 Tennis</h1>
<div class="score">Spieler: <span id="playerScore">0</span> | Computer: <span id="computerScore">0</span></div>
<canvas id="gameCanvas"></canvas>
<div class="controls">
<button class="btn" id="startBtn" onclick="startGame()">▶️ Start</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<p class="instructions">💡 Klicke/Tippe auf den Bildschirm, um das Paddle zu bewegen!</p>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Responsive Größe
function resizeCanvas() {
const maxWidth = Math.min(window.innerWidth - 20, 600);
const maxHeight = Math.min(window.innerHeight - 200, 400);
canvas.width = maxWidth;
canvas.height = maxHeight;
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// Spiel-Variablen
let gameRunning = false;
let playerScore = 0;
let computerScore = 0;
let animationId;
// Paddle
const paddleWidth = 10;
const paddleHeight = 80;
let playerY = canvas.height / 2 - paddleHeight / 2;
let computerY = canvas.height / 2 - paddleHeight / 2;
const computerSpeed = 3;
// Ball
const ballSize = 10;
let ballX = canvas.width / 2;
let ballY = canvas.height / 2;
let ballSpeedX = 5;
let ballSpeedY = 3;
// Maus/Touch Position
let targetY = canvas.height / 2;
// Input-Handler
canvas.addEventListener('mousemove', (e) => {
const rect = canvas.getBoundingClientRect();
targetY = e.clientY - rect.top - paddleHeight / 2;
});
canvas.addEventListener('touchmove', (e) => {
e.preventDefault();
const rect = canvas.getBoundingClientRect();
targetY = e.touches[0].clientY - rect.top - paddleHeight / 2;
}, { passive: false });
canvas.addEventListener('click', () => {
if (!gameRunning) startGame();
});
function resetBall() {
ballX = canvas.width / 2;
ballY = canvas.height / 2;
ballSpeedX = (Math.random() > 0.5 ? 1 : -1) * (4 + Math.random() * 2);
ballSpeedY = (Math.random() * 2 - 1) * 4;
}
function update() {
if (!gameRunning) return;
// Spieler-Paddle bewegen (smooth)
playerY += (targetY - playerY) * 0.2;
playerY = Math.max(0, Math.min(canvas.height - paddleHeight, playerY));
// Computer-Paddle bewegen
const computerCenter = computerY + paddleHeight / 2;
if (computerCenter < ballY - 10) computerY += computerSpeed;
else if (computerCenter > ballY + 10) computerY -= computerSpeed;
computerY = Math.max(0, Math.min(canvas.height - paddleHeight, computerY));
// Ball bewegen
ballX += ballSpeedX;
ballY += ballSpeedY;
// Wände
if (ballY <= 0 || ballY >= canvas.height - ballSize) {
ballSpeedY = -ballSpeedY;
}
// Spieler-Paddle Kollision
if (ballX <= paddleWidth + ballSize &&
ballY > playerY && ballY < playerY + paddleHeight) {
ballSpeedX = -ballSpeedX * 1.05;
ballSpeedY += (ballY - (playerY + paddleHeight / 2)) * 0.1;
}
// Computer-Paddle Kollision
if (ballX >= canvas.width - paddleWidth - ballSize &&
ballY > computerY && ballY < computerY + paddleHeight) {
ballSpeedX = -ballSpeedX * 1.05;
ballSpeedY += (ballY - (computerY + paddleHeight / 2)) * 0.1;
}
// Punkt
if (ballX < 0) {
computerScore++;
document.getElementById('computerScore').textContent = computerScore;
resetBall();
} else if (ballX > canvas.width) {
playerScore++;
document.getElementById('playerScore').textContent = playerScore;
resetBall();
}
}
function draw() {
// Hintergrund
ctx.fillStyle = '#4ade80';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Mittellinie
ctx.strokeStyle = 'rgba(255,255,255,0.5)';
ctx.setLineDash([10, 10]);
ctx.beginPath();
ctx.moveTo(canvas.width / 2, 0);
ctx.lineTo(canvas.width / 2, canvas.height);
ctx.stroke();
ctx.setLineDash([]);
// Paddles
ctx.fillStyle = 'white';
ctx.fillRect(0, playerY, paddleWidth, paddleHeight);
ctx.fillRect(canvas.width - paddleWidth, computerY, paddleWidth, paddleHeight);
// Ball
ctx.beginPath();
ctx.arc(ballX, ballY, ballSize, 0, Math.PI * 2);
ctx.fillStyle = '#ff6b6b';
ctx.fill();
ctx.strokeStyle = 'white';
ctx.lineWidth = 2;
ctx.stroke();
// Start-Hinweis
if (!gameRunning) {
ctx.fillStyle = 'rgba(0,0,0,0.5)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'white';
ctx.font = 'bold 24px Comic Sans MS';
ctx.textAlign = 'center';
ctx.fillText('Klicke auf Start!', canvas.width / 2, canvas.height / 2);
}
}
function gameLoop() {
update();
draw();
animationId = requestAnimationFrame(gameLoop);
}
function startGame() {
gameRunning = true;
document.getElementById('startBtn').textContent = '🔄 Neustart';
resetBall();
}
// Start
gameLoop();
</script>
</body>
</html>
+341
View File
@@ -0,0 +1,341 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Tetris - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; touch-action: manipulation; }
body {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 5px;
}
h1 { color: white; margin: 5px 0; font-size: 20px; }
.score-board { color: #4ade80; font-size: 16px; margin-bottom: 5px; }
.game-container { display: flex; gap: 10px; align-items: flex-start; flex-wrap: wrap; justify-content: center; }
#gameCanvas {
background: #0f0f23; border: 3px solid #4ade80; border-radius: 8px;
box-shadow: 0 0 15px rgba(74, 222, 128, 0.3); touch-action: none;
}
.side-panel { display: flex; flex-direction: column; gap: 10px; }
.next-piece {
background: #1a1a2e; border: 2px solid #4ade80; border-radius: 8px;
padding: 8px; text-align: center;
}
.next-piece h3 { color: white; margin-bottom: 5px; font-size: 12px; }
#nextCanvas { background: #0f0f23; }
.controls-panel {
display: flex; flex-direction: column; gap: 8px;
}
.btn {
background: linear-gradient(135deg, #4ade80, #22c55e); border: none; border-radius: 8px;
padding: 10px 15px; font-size: 14px; cursor: pointer; font-family: inherit; font-weight: bold;
color: white; box-shadow: 0 3px 8px rgba(0,0,0,0.3);
}
.btn:active { transform: scale(0.95); }
.btn-back { background: #ff6b6b; text-decoration: none; display: inline-block; text-align: center; }
/* Touch Controls - D-Pad Style */
.touch-controls {
display: grid;
grid-template-columns: 60px 60px 60px;
gap: 5px;
margin-top: 10px;
}
.touch-btn {
width: 60px; height: 60px;
background: rgba(255,255,255,0.15);
border: 2px solid #4ade80;
border-radius: 10px;
font-size: 28px;
color: #4ade80;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
touch-action: manipulation;
}
.touch-btn:active {
background: rgba(74, 222, 128, 0.3);
transform: scale(0.95);
}
.touch-btn.action {
background: rgba(74, 222, 128, 0.25);
font-size: 22px;
}
@media (min-width: 600px) {
h1 { font-size: 24px; }
.score-board { font-size: 18px; }
}
</style>
</head>
<body>
<h1>🧱 Tetris</h1>
<div class="score-board">Punkte: <span id="score">0</span> | Level: <span id="level">1</span></div>
<div class="game-container">
<canvas id="gameCanvas" width="240" height="480"></canvas>
<div class="side-panel">
<div class="next-piece">
<h3>Nächstes</h3>
<canvas id="nextCanvas" width="80" height="80"></canvas>
</div>
<div class="controls-panel">
<button class="btn" id="startBtn" onclick="startGame()">▶️ Start</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<!-- Touch Controls -->
<div class="touch-controls">
<div></div>
<button class="touch-btn" ontouchstart="rotatePiece(); return false;" onclick="rotatePiece()"></button>
<div></div>
<button class="touch-btn" ontouchstart="movePiece(-1, 0); return false;" onclick="movePiece(-1, 0)"></button>
<button class="touch-btn action" ontouchstart="hardDrop(); return false;" onclick="hardDrop()"></button>
<button class="touch-btn" ontouchstart="movePiece(1, 0); return false;" onclick="movePiece(1, 0)"></button>
<div></div>
<button class="touch-btn" ontouchstart="movePiece(0, 1); return false;" onclick="movePiece(0, 1)"></button>
<div></div>
</div>
</div>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const nextCanvas = document.getElementById('nextCanvas');
const nextCtx = nextCanvas.getContext('2d');
// Game variables
const COLS = 10;
const ROWS = 20;
const BLOCK_SIZE = 24;
let board = Array(ROWS).fill(null).map(() => Array(COLS).fill(0));
let score = 0;
let level = 1;
let gameRunning = false;
let dropInterval = 1000;
let lastDrop = 0;
let animationId;
// Tetrominos
const PIECES = [
[[1,1,1,1]], [[1,1],[1,1]], [[0,1,0],[1,1,1]],
[[1,1,0],[0,1,1]], [[0,1,1],[1,1,0]],
[[1,0,0],[1,1,1]], [[0,0,1],[1,1,1]]
];
const COLORS = ['#00f0f0', '#f0f000', '#a000f0', '#00f000', '#f00000', '#f0a000', '#0000f0'];
let currentPiece = null;
let nextPiece = null;
let currentX = 0, currentY = 0, currentColor = 0;
function createPiece() {
const type = Math.floor(Math.random() * PIECES.length);
return { shape: PIECES[type], color: type };
}
function spawnPiece() {
currentPiece = nextPiece || createPiece();
nextPiece = createPiece();
currentX = Math.floor(COLS / 2) - Math.floor(currentPiece.shape[0].length / 2);
currentY = 0;
currentColor = currentPiece.color;
drawNext();
if (collision(currentPiece.shape, currentX, currentY)) {
gameOver();
}
}
function drawNext() {
nextCtx.fillStyle = '#0f0f23';
nextCtx.fillRect(0, 0, nextCanvas.width, nextCanvas.height);
if (!nextPiece) return;
const blockSize = 18;
const offsetX = (nextCanvas.width - nextPiece.shape[0].length * blockSize) / 2;
const offsetY = (nextCanvas.height - nextPiece.shape.length * blockSize) / 2;
nextCtx.fillStyle = COLORS[nextPiece.color];
nextCtx.strokeStyle = 'white';
for (let y = 0; y < nextPiece.shape.length; y++) {
for (let x = 0; x < nextPiece.shape[y].length; x++) {
if (nextPiece.shape[y][x]) {
nextCtx.fillRect(offsetX + x * blockSize, offsetY + y * blockSize, blockSize - 1, blockSize - 1);
nextCtx.strokeRect(offsetX + x * blockSize, offsetY + y * blockSize, blockSize - 1, blockSize - 1);
}
}
}
}
function collision(shape, x, y) {
for (let row = 0; row < shape.length; row++) {
for (let col = 0; col < shape[row].length; col++) {
if (shape[row][col]) {
const newX = x + col;
const newY = y + row;
if (newX < 0 || newX >= COLS || newY >= ROWS) return true;
if (newY >= 0 && board[newY][newX]) return true;
}
}
}
return false;
}
function mergePiece() {
for (let y = 0; y < currentPiece.shape.length; y++) {
for (let x = 0; x < currentPiece.shape[y].length; x++) {
if (currentPiece.shape[y][x]) {
const boardY = currentY + y;
const boardX = currentX + x;
if (boardY >= 0) board[boardY][boardX] = currentColor + 1;
}
}
}
}
function clearLines() {
let linesCleared = 0;
for (let y = ROWS - 1; y >= 0; y--) {
if (board[y].every(cell => cell !== 0)) {
board.splice(y, 1);
board.unshift(Array(COLS).fill(0));
linesCleared++;
y++;
}
}
if (linesCleared > 0) {
score += linesCleared * 100 * level;
level = Math.floor(score / 1000) + 1;
dropInterval = Math.max(100, 1000 - (level - 1) * 80);
document.getElementById('score').textContent = score;
document.getElementById('level').textContent = level;
}
}
function rotatePiece() {
if (!gameRunning || !currentPiece) return;
const rotated = currentPiece.shape[0].map((_, i) =>
currentPiece.shape.map(row => row[i]).reverse()
);
if (!collision(rotated, currentX, currentY)) {
currentPiece.shape = rotated;
}
}
function movePiece(dx, dy) {
if (!gameRunning || !currentPiece) return;
const newX = currentX + dx;
const newY = currentY + dy;
if (!collision(currentPiece.shape, newX, newY)) {
currentX = newX;
currentY = newY;
} else if (dy > 0) {
mergePiece();
clearLines();
spawnPiece();
}
}
function hardDrop() {
if (!gameRunning) return;
while (!collision(currentPiece.shape, currentX, currentY + 1)) {
currentY++;
score += 2;
}
document.getElementById('score').textContent = score;
movePiece(0, 1);
}
function draw() {
// Hintergrund
ctx.fillStyle = '#0f0f23';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Board
for (let y = 0; y < ROWS; y++) {
for (let x = 0; x < COLS; x++) {
if (board[y][x]) {
ctx.fillStyle = COLORS[board[y][x] - 1];
ctx.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE - 1, BLOCK_SIZE - 1);
}
}
}
// Aktuelles Piece
if (currentPiece) {
ctx.fillStyle = COLORS[currentColor];
for (let y = 0; y < currentPiece.shape.length; y++) {
for (let x = 0; x < currentPiece.shape[y].length; x++) {
if (currentPiece.shape[y][x]) {
const px = (currentX + x) * BLOCK_SIZE;
const py = (currentY + y) * BLOCK_SIZE;
ctx.fillRect(px, py, BLOCK_SIZE - 1, BLOCK_SIZE - 1);
ctx.strokeStyle = 'rgba(255,255,255,0.3)';
ctx.strokeRect(px, py, BLOCK_SIZE - 1, BLOCK_SIZE - 1);
}
}
}
}
}
function gameLoop(timestamp) {
if (gameRunning) {
if (timestamp - lastDrop > dropInterval) {
movePiece(0, 1);
lastDrop = timestamp;
}
draw();
}
animationId = requestAnimationFrame(gameLoop);
}
function startGame() {
board = Array(ROWS).fill(null).map(() => Array(COLS).fill(0));
score = 0;
level = 1;
dropInterval = 1000;
gameRunning = true;
document.getElementById('score').textContent = score;
document.getElementById('level').textContent = level;
document.getElementById('startBtn').textContent = '🔄 Neustart';
spawnPiece();
}
function gameOver() {
gameRunning = false;
alert('Game Over! Punkte: ' + score);
}
// Keyboard controls
document.addEventListener('keydown', (e) => {
if (!gameRunning) return;
switch(e.key) {
case 'ArrowLeft': case 'a': movePiece(-1, 0); break;
case 'ArrowRight': case 'd': movePiece(1, 0); break;
case 'ArrowDown': case 's': movePiece(0, 1); break;
case 'ArrowUp': case 'w': case ' ': rotatePiece(); break;
}
});
// Start
gameLoop(0);
</script>
</body>
</html>
+188
View File
@@ -0,0 +1,188 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Tierlaute - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #22c55e 0%, #16a34a 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 20px;
}
h1 { color: white; margin-bottom: 15px; font-size: 28px; }
.question { color: white; font-size: 22px; margin: 20px 0; text-align: center; }
.sound-btn {
background: linear-gradient(135deg, #fbbf24, #f59e0b); border: none; border-radius: 50%;
width: 120px; height: 120px; font-size: 60px; cursor: pointer;
box-shadow: 0 10px 30px rgba(0,0,0,0.3); margin: 20px 0;
transition: transform 0.2s;
}
.sound-btn:hover { transform: scale(1.1); }
.sound-btn:active { transform: scale(0.9); }
.animals-grid {
display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px;
max-width: 400px; width: 100%; margin: 20px 0;
}
.animal-card {
background: white; border-radius: 20px; padding: 20px;
display: flex; flex-direction: column; align-items: center;
cursor: pointer; transition: all 0.2s; box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.animal-card:hover { transform: translateY(-5px); }
.animal-card:active { transform: scale(0.95); }
.animal-card.correct { background: #86efac; }
.animal-card.wrong { background: #fca5a5; }
.animal-emoji { font-size: 50px; margin-bottom: 5px; }
.animal-name { font-size: 18px; color: #333; font-weight: bold; }
.score { color: white; font-size: 20px; margin: 10px 0; }
.feedback { font-size: 36px; margin: 10px 0; min-height: 50px; }
.controls { display: flex; gap: 15px; margin-top: 20px; }
.btn {
background: white; border: none; border-radius: 10px; padding: 12px 25px;
font-size: 16px; cursor: pointer; font-family: inherit; font-weight: bold;
}
.btn-back { background: #ff6b6b; color: white; text-decoration: none; }
</style>
</head>
<body>
<h1>🦁 Tierlaute</h1>
<div class="score">Richtig: <span id="correct">0</span> | Versuche: <span id="total">0</span></div>
<div class="question">Welches Tier macht dieses Geräusch?</div>
<button class="sound-btn" onclick="playCurrentSound()">🔊</button>
<div class="feedback" id="feedback"></div>
<div class="animals-grid" id="animalsGrid"></div>
<div class="controls">
<button class="btn" onclick="nextQuestion()">🔄 Nächstes Tier</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<script>
const animals = [
{ emoji: '🐶', name: 'Hund', sound: 'Wau wau!' },
{ emoji: '🐱', name: 'Katze', sound: 'Miau!' },
{ emoji: '🐮', name: 'Kuh', sound: 'Muh!' },
{ emoji: '🐷', name: 'Schwein', sound: 'Oink oink!' },
{ emoji: '🐔', name: 'Huhn', sound: 'Gack gack!' },
{ emoji: '🐴', name: 'Pferd', sound: 'Wieher!' },
{ emoji: '🐸', name: 'Frosch', sound: 'Quak quak!' },
{ emoji: '🦆', name: 'Ente', sound: 'Quack quack!' },
{ emoji: '🐘', name: 'Elefant', sound: 'Törööö!' },
{ emoji: '🦁', name: 'Löwe', sound: 'Roooar!' },
{ emoji: '🐍', name: 'Schlange', sound: 'Zisch!' },
{ emoji: '🦉', name: 'Eule', sound: 'Huhuhu!' }
];
let currentAnimal = null;
let correctCount = 0;
let totalCount = 0;
let answered = false;
const synth = window.speechSynthesis;
function initGame() {
nextQuestion();
}
function nextQuestion() {
answered = false;
document.getElementById('feedback').textContent = '';
// Zufälliges Tier
currentAnimal = animals[Math.floor(Math.random() * animals.length)];
// 4 Optionen (inkl. richtige)
let options = [currentAnimal];
while (options.length < 4) {
const random = animals[Math.floor(Math.random() * animals.length)];
if (!options.find(o => o.name === random.name)) {
options.push(random);
}
}
// Mischen
options.sort(() => Math.random() - 0.5);
// Grid bauen
const grid = document.getElementById('animalsGrid');
grid.innerHTML = '';
options.forEach(animal => {
const card = document.createElement('div');
card.className = 'animal-card';
card.innerHTML = `
<div class="animal-emoji">${animal.emoji}</div>
<div class="animal-name">${animal.name}</div>
`;
card.onclick = () => checkAnswer(animal, card);
grid.appendChild(card);
});
// Auto-play nach kurzer Zeit
setTimeout(playCurrentSound, 500);
}
function playCurrentSound() {
if (!currentAnimal) return;
// TTS für Tier-Sound
const utterance = new SpeechSynthesisUtterance(currentAnimal.sound);
utterance.lang = 'de-DE';
utterance.rate = 0.8;
utterance.pitch = 1.2;
synth.speak(utterance);
}
function checkAnswer(selected, cardElement) {
if (answered) return;
answered = true;
totalCount++;
document.getElementById('total').textContent = totalCount;
if (selected.name === currentAnimal.name) {
correctCount++;
document.getElementById('correct').textContent = correctCount;
cardElement.classList.add('correct');
document.getElementById('feedback').textContent = '🎉 Richtig!';
const utterance = new SpeechSynthesisUtterance('Richtig! ' + currentAnimal.sound);
utterance.lang = 'de-DE';
utterance.rate = 0.8;
synth.speak(utterance);
setTimeout(nextQuestion, 2000);
} else {
cardElement.classList.add('wrong');
document.getElementById('feedback').textContent = '❌ Das ist ' + selected.name;
// Richtige hervorheben
const cards = document.querySelectorAll('.animal-card');
cards.forEach(card => {
if (card.textContent.includes(currentAnimal.name)) {
card.classList.add('correct');
}
});
const utterance = new SpeechSynthesisUtterance('Das ist ' + selected.name + '. Höre nochmal!');
utterance.lang = 'de-DE';
synth.speak(utterance);
setTimeout(() => {
answered = false;
playCurrentSound();
}, 2000);
}
}
// Init
initGame();
</script>
</body>
</html>
+600
View File
@@ -0,0 +1,600 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Uno - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #1a202c 0%, #2d3748 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 10px;
}
h1 { color: white; margin-bottom: 5px; font-size: 24px; }
.game-info {
display: flex; gap: 20px; margin: 10px 0; color: white; font-size: 14px;
}
.info-item { text-align: center; }
.info-value { font-size: 20px; font-weight: bold; color: #feca57; }
/* Karten-Stapel */
.deck-area {
display: flex; gap: 30px; margin: 15px 0; align-items: center;
}
.card-pile {
width: 80px; height: 120px; background: linear-gradient(135deg, #333, #555);
border-radius: 10px; border: 3px solid #fff;
display: flex; align-items: center; justify-content: center;
font-size: 30px; cursor: pointer; position: relative;
box-shadow: 0 5px 15px rgba(0,0,0,0.5);
}
.card-pile::after {
content: ''; position: absolute; top: -3px; left: -3px;
width: 80px; height: 120px; background: linear-gradient(135deg, #333, #555);
border-radius: 10px; border: 3px solid #fff; z-index: -1;
}
.discard-pile {
width: 80px; height: 120px; border-radius: 10px; border: 3px solid #fff;
display: flex; flex-direction: column; align-items: center; justify-content: center;
font-size: 32px; font-weight: bold; box-shadow: 0 5px 15px rgba(0,0,0,0.5);
color: white;
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
}
.discard-pile.red { background: #E53E3E; }
.discard-pile.yellow { background: #D69E2E; }
.discard-pile.green { background: #38A169; }
.discard-pile.blue { background: #3182CE; }
.discard-pile.wild {
background: linear-gradient(45deg, #E53E3E, #D69E2E, #38A169, #3182CE);
}
/* Bot-Hand */
.bot-hand {
display: flex; gap: -20px; margin: 10px 0; flex-wrap: wrap; justify-content: center;
}
.bot-card {
width: 50px; height: 75px; background: linear-gradient(135deg, #333, #000);
border: 2px solid #fff; border-radius: 8px;
display: flex; align-items: center; justify-content: center;
font-size: 20px; margin-left: -20px;
box-shadow: 2px 2px 5px rgba(0,0,0,0.3);
}
.bot-card:first-child { margin-left: 0; }
/* Spieler-Hand */
.player-hand {
display: flex; gap: 10px; margin: 15px 0; flex-wrap: wrap; justify-content: center;
max-width: 400px;
}
.card {
width: 70px; height: 100px; border-radius: 10px; border: 3px solid #fff;
display: flex; flex-direction: column; align-items: center; justify-content: center;
font-size: 28px; font-weight: bold; cursor: pointer;
transition: all 0.2s; box-shadow: 0 5px 15px rgba(0,0,0,0.3);
position: relative;
}
.card:hover { transform: translateY(-10px); }
.card.disabled { opacity: 0.5; cursor: not-allowed; }
.card:not(.disabled):hover { transform: translateY(-10px) scale(1.1); }
/* Farben */
.card.red { background: #E53E3E; color: white; }
.card.yellow { background: #D69E2E; color: white; }
.card.green { background: #38A169; color: white; }
.card.blue { background: #3182CE; color: white; }
.card.wild { background: linear-gradient(45deg, #E53E3E, #D69E2E, #38A169, #3182CE); color: white; }
.card-value { font-size: 32px; }
.card-corner {
position: absolute; font-size: 12px;
top: 5px; left: 5px;
}
.card-corner-bottom {
position: absolute; font-size: 12px;
bottom: 5px; right: 5px; transform: rotate(180deg);
}
.status {
color: white; font-size: 16px; margin: 10px 0; text-align: center;
min-height: 40px;
}
.color-picker {
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: white; padding: 20px; border-radius: 15px;
display: none; z-index: 100; text-align: center;
box-shadow: 0 10px 50px rgba(0,0,0,0.5);
}
.color-picker h3 { margin-bottom: 15px; }
.color-options { display: flex; gap: 10px; }
.color-option {
width: 60px; height: 60px; border-radius: 10px; cursor: pointer;
border: 3px solid transparent;
}
.color-option:hover { transform: scale(1.1); border-color: #333; }
.direction-indicator {
font-size: 30px; margin: 5px 0;
}
.controls { display: flex; gap: 15px; margin-top: 15px; }
.btn {
background: white; border: none; border-radius: 10px; padding: 10px 20px;
font-size: 14px; cursor: pointer; font-family: inherit; font-weight: bold;
}
.btn-draw { background: #4ade80; color: white; }
.btn-back { background: #ff6b6b; color: white; text-decoration: none; }
.game-over {
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: white; padding: 30px; border-radius: 20px; text-align: center;
display: none; z-index: 100; box-shadow: 0 10px 50px rgba(0,0,0,0.5);
}
.game-over h2 { margin-bottom: 15px; }
</style>
</head>
<body>
<h1>🎴 Uno</h1>
<div class="game-info">
<div class="info-item">
<div>Computer</div>
<div class="info-value" id="botCardCount">7</div>
</div>
<div class="info-item">
<div>Stapel</div>
<div class="info-value" id="deckCount">79</div>
</div>
<div class="info-item">
<div>Richtung</div>
<div class="direction-indicator" id="direction">➡️</div>
</div>
</div>
<div class="bot-hand" id="botHand"></div>
<div class="deck-area">
<div class="card-pile" id="deck" onclick="drawCard()">
🎴
</div>
<div class="discard-pile" id="discard">
<span id="topCard">🎴</span>
</div>
</div>
<div class="status" id="status">Wähle eine Karte oder ziehe vom Stapel!</div>
<div class="player-hand" id="playerHand"></div>
<div class="color-picker" id="colorPicker">
<h3>🎨 Wähle eine Farbe:</h3>
<div class="color-options">
<div class="color-option" style="background:#E53E3E" onclick="selectColor('red')"></div>
<div class="color-option" style="background:#D69E2E" onclick="selectColor('yellow')"></div>
<div class="color-option" style="background:#38A169" onclick="selectColor('green')"></div>
<div class="color-option" style="background:#3182CE" onclick="selectColor('blue')"></div>
</div>
</div>
<div class="controls">
<button class="btn btn-draw" onclick="drawCard()">📥 Karte ziehen</button>
<button class="btn" onclick="newGame()">🔄 Neustart</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<div class="game-over" id="gameOver">
<h2 id="winnerText">🎉 Gewonnen!</h2>
<button class="btn btn-draw" onclick="newGame()">🔄 Nochmal</button>
</div>
<script>
const COLORS = ['red', 'yellow', 'green', 'blue'];
const VALUES = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'skip', 'reverse', '+2'];
const EMOJIS = {
'0': '0', '1': '1', '2': '2', '3': '3', '4': '4',
'5': '5', '6': '6', '7': '7', '8': '8', '9': '9',
'skip': '🚫', 'reverse': '🔄', '+2': '+2', '+4': '+4', 'wild': '🌈'
};
let deck = [];
let playerHand = [];
let botHand = [];
let discardPile = [];
let currentColor = null;
let currentValue = null;
let isPlayerTurn = true;
let direction = 1; // 1 = vorwärts, -1 = rückwärts
let cardsToDraw = 0;
let waitingForColor = false;
let pendingWildCard = null;
function createDeck() {
deck = [];
// Farbkarten (jede Farbe 0-9, +2, skip, reverse)
COLORS.forEach(color => {
// Eine 0 pro Farbe
deck.push({ color, value: '0', emoji: '0' });
// Zwei von jeder anderen Karte
for (let i = 1; i <= 9; i++) {
deck.push({ color, value: i.toString(), emoji: i.toString() });
deck.push({ color, value: i.toString(), emoji: i.toString() });
}
// Spezialkarten
['skip', 'reverse', '+2'].forEach(val => {
deck.push({ color, value: val, emoji: EMOJIS[val] });
deck.push({ color, value: val, emoji: EMOJIS[val] });
});
});
// Joker (wild und +4)
for (let i = 0; i < 4; i++) {
deck.push({ color: 'wild', value: 'wild', emoji: '🌈' });
deck.push({ color: 'wild', value: '+4', emoji: '+4' });
}
return shuffle(deck);
}
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
function initGame() {
deck = createDeck();
playerHand = [];
botHand = [];
discardPile = [];
direction = 1;
cardsToDraw = 0;
waitingForColor = false;
// Karten austeilen
for (let i = 0; i < 7; i++) {
playerHand.push(deck.pop());
botHand.push(deck.pop());
}
// Startkarte (kein Joker)
let startCard;
do {
startCard = deck.pop();
} while (startCard.color === 'wild');
discardPile.push(startCard);
currentColor = startCard.color;
currentValue = startCard.value;
isPlayerTurn = true;
updateDisplay();
document.getElementById('gameOver').style.display = 'none';
}
function updateDisplay() {
// Bot-Hand
const botHandEl = document.getElementById('botHand');
botHandEl.innerHTML = '';
botHand.forEach(() => {
const card = document.createElement('div');
card.className = 'bot-card';
card.textContent = '🎴';
botHandEl.appendChild(card);
});
// Spieler-Hand
const playerHandEl = document.getElementById('playerHand');
playerHandEl.innerHTML = '';
playerHand.forEach((card, idx) => {
const cardEl = document.createElement('div');
cardEl.className = `card ${card.color}`;
// Prüfen ob spielbar
const canPlay = isPlayerTurn && !waitingForColor &&
(card.color === currentColor || card.value === currentValue || card.color === 'wild');
if (!canPlay) cardEl.classList.add('disabled');
cardEl.innerHTML = `
<span class="card-corner">${card.emoji}</span>
<span class="card-value">${card.emoji}</span>
<span class="card-corner-bottom">${card.emoji}</span>
`;
if (canPlay) {
cardEl.onclick = () => playCard(idx);
}
playerHandEl.appendChild(cardEl);
});
// Oberste Karte
const topCard = discardPile[discardPile.length - 1];
const discardEl = document.getElementById('discard');
const cardColor = topCard.color === 'wild' ? currentColor : topCard.color;
discardEl.className = `discard-pile ${cardColor}`;
// Zeige Farbnamen und Wert
const colorNames = { red: '🔴 ROT', yellow: '🟡 GELB', green: '🟢 GRÜN', blue: '🔵 BLAU' };
const displayText = topCard.color === 'wild' ? topCard.emoji : `${topCard.emoji}`;
document.getElementById('topCard').innerHTML = `
<div style="font-size:28px;margin-bottom:5px">${displayText}</div>
<div style="font-size:10px;text-transform:uppercase">${colorNames[cardColor] || cardColor}</div>
`;
// Info
document.getElementById('botCardCount').textContent = botHand.length;
document.getElementById('deckCount').textContent = deck.length;
document.getElementById('direction').textContent = direction === 1 ? '➡️' : '⬅️';
// Status
const statusEl = document.getElementById('status');
if (waitingForColor) {
statusEl.textContent = 'Wähle eine Farbe für den Joker!';
} else if (cardsToDraw > 0) {
statusEl.textContent = `Du musst ${cardsToDraw} Karten ziehen!`;
} else if (isPlayerTurn) {
statusEl.textContent = 'Du bist dran! Wähle eine Karte.';
} else {
statusEl.textContent = 'Computer denkt...';
}
}
function playCard(handIdx) {
if (!isPlayerTurn || waitingForColor) return;
const card = playerHand[handIdx];
// Prüfen ob Karte spielbar
if (!canPlayCard(card)) {
speak('Diese Karte kannst du nicht spielen!');
return;
}
// Karte spielen
playerHand.splice(handIdx, 1);
discardPile.push(card);
// Joker?
if (card.color === 'wild') {
waitingForColor = true;
pendingWildCard = card;
document.getElementById('colorPicker').style.display = 'block';
updateDisplay();
return;
}
// Normale Karte
currentColor = card.color;
currentValue = card.value;
applyCardEffect(card);
// Gewonnen?
if (playerHand.length === 0) {
endGame(true);
return;
}
updateDisplay();
if (!isPlayerTurn) {
setTimeout(botTurn, 1500);
}
}
function canPlayCard(card) {
if (cardsToDraw > 0) {
// Muss +2 oder +4 spielen
return card.value === '+2' || card.value === '+4';
}
return card.color === currentColor ||
card.value === currentValue ||
card.color === 'wild';
}
function selectColor(color) {
if (!waitingForColor) return;
currentColor = color;
currentValue = pendingWildCard.value;
waitingForColor = false;
document.getElementById('colorPicker').style.display = 'none';
applyCardEffect(pendingWildCard);
pendingWildCard = null;
updateDisplay();
if (!isPlayerTurn) {
setTimeout(botTurn, 1000);
}
}
function applyCardEffect(card) {
switch(card.value) {
case 'skip':
speak('Aussetzen!');
// Gegner überspringen
break;
case 'reverse':
speak('Richtungswechsel!');
direction *= -1;
break;
case '+2':
speak('+2! Computer zieht 2 Karten!');
cardsToDraw = 2;
break;
case '+4':
speak('+4! Computer zieht 4 Karten!');
cardsToDraw = 4;
break;
default:
speak(card.emoji);
}
isPlayerTurn = !isPlayerTurn;
}
function drawCard() {
if (!isPlayerTurn || waitingForColor) return;
if (deck.length === 0) {
// Stapel neu mischen
if (discardPile.length <= 1) return;
const topCard = discardPile.pop();
deck = shuffle(discardPile);
discardPile = [topCard];
}
// Karte ziehen
const drawn = deck.pop();
if (cardsToDraw > 0) {
// Muss Karten ziehen
playerHand.push(drawn);
cardsToDraw--;
if (cardsToDraw === 0) {
// Nächster Spieler
isPlayerTurn = false;
setTimeout(botTurn, 1000);
}
} else {
playerHand.push(drawn);
// Nach Ziehen: Nächster Spieler
isPlayerTurn = false;
setTimeout(botTurn, 1000);
}
updateDisplay();
}
function botTurn() {
if (isPlayerTurn || waitingForColor) return;
// Spielbare Karten finden
const playableCards = botHand.map((card, idx) => ({ card, idx }))
.filter(({ card }) => canPlayCard(card));
if (playableCards.length > 0) {
// Beste Karte wählen (Priorität: +4, +2, Spezial, Zahlen)
let bestIdx = playableCards[0].idx;
let bestPriority = -1;
playableCards.forEach(({ card, idx }) => {
let priority = 0;
if (card.value === '+4') priority = 4;
else if (card.value === '+2') priority = 3;
else if (['skip', 'reverse'].includes(card.value)) priority = 2;
else if (card.color === 'wild') priority = 1;
if (priority > bestPriority) {
bestPriority = priority;
bestIdx = idx;
}
});
const card = botHand[bestIdx];
botHand.splice(bestIdx, 1);
discardPile.push(card);
// Farbe wählen (meiste in Hand)
if (card.color === 'wild') {
const colorCounts = {};
botHand.forEach(c => {
if (c.color !== 'wild') colorCounts[c.color] = (colorCounts[c.color] || 0) + 1;
});
const bestColor = Object.entries(colorCounts)
.sort((a, b) => b[1] - a[1])[0]?.[0] || 'red';
currentColor = bestColor;
currentValue = card.value;
} else {
currentColor = card.color;
currentValue = card.value;
}
speak(`Computer spielt ${card.emoji}`);
applyCardEffect(card);
if (botHand.length === 0) {
endGame(false);
return;
}
} else {
// Karte ziehen
if (deck.length === 0) {
const topCard = discardPile.pop();
deck = shuffle(discardPile);
discardPile = [topCard];
}
const drawn = deck.pop();
if (cardsToDraw > 0) {
for (let i = 0; i < cardsToDraw && deck.length > 0; i++) {
botHand.push(deck.pop());
}
speak(`Computer zieht ${cardsToDraw} Karten!`);
cardsToDraw = 0;
} else {
botHand.push(drawn);
speak('Computer zieht eine Karte');
}
isPlayerTurn = true;
}
updateDisplay();
}
function endGame(playerWon) {
const modal = document.getElementById('gameOver');
const winnerText = document.getElementById('winnerText');
if (playerWon) {
winnerText.textContent = '🎉 Du hast gewonnen!';
speak('Glückwunsch! Du hast gewonnen!');
} else {
winnerText.textContent = '🤖 Computer hat gewonnen!';
speak('Computer hat gewonnen!');
}
modal.style.display = 'block';
}
function newGame() {
initGame();
}
function speak(text) {
if ('speechSynthesis' in window) {
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'de-DE';
speechSynthesis.speak(utterance);
}
}
// Init
initGame();
</script>
</body>
</html>
+251
View File
@@ -0,0 +1,251 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Würfel - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 20px;
}
h1 { color: white; margin-bottom: 15px; font-size: 26px; }
.dice-container {
perspective: 1000px; margin: 30px 0;
}
.dice {
width: 100px; height: 100px; position: relative;
transform-style: preserve-3d; transition: transform 1s ease-out;
}
.dice.rolling { animation: roll 1s ease-out; }
@keyframes roll {
0% { transform: rotateX(0) rotateY(0) rotateZ(0); }
100% { transform: rotateX(720deg) rotateY(720deg) rotateZ(360deg); }
}
.face {
position: absolute; width: 100px; height: 100px;
background: white; border: 3px solid #333; border-radius: 15px;
box-shadow: inset 0 0 20px rgba(0,0,0,0.2);
}
.face-1 { transform: rotateY(0deg) translateZ(50px); }
.face-2 { transform: rotateY(90deg) translateZ(50px); }
.face-3 { transform: rotateY(180deg) translateZ(50px); }
.face-4 { transform: rotateY(-90deg) translateZ(50px); }
.face-5 { transform: rotateX(90deg) translateZ(50px); }
.face-6 { transform: rotateX(-90deg) translateZ(50px); }
.dot {
width: 20px; height: 20px; background: #333; border-radius: 50%;
position: absolute; box-shadow: inset 2px 2px 4px rgba(0,0,0,0.3);
}
/* Seite 1: Eins in der Mitte */
.face-1 .dot1 { top: 50%; left: 50%; transform: translate(-50%, -50%); }
/* Seite 2: Zwei in diagonalen Ecken */
.face-2 .dot1 { top: 20%; left: 20%; }
.face-2 .dot2 { bottom: 20%; right: 20%; }
/* Seite 3: Drei diagonal */
.face-3 .dot1 { top: 20%; left: 20%; }
.face-3 .dot2 { top: 50%; left: 50%; transform: translate(-50%, -50%); }
.face-3 .dot3 { bottom: 20%; right: 20%; }
/* Seite 4: Vier in allen Ecken */
.face-4 .dot1 { top: 20%; left: 20%; }
.face-4 .dot2 { top: 20%; right: 20%; }
.face-4 .dot3 { bottom: 20%; left: 20%; }
.face-4 .dot4 { bottom: 20%; right: 20%; }
/* Seite 5: Fünf (Vier Ecken + Mitte) */
.face-5 .dot1 { top: 20%; left: 20%; } /* Oben links */
.face-5 .dot2 { top: 20%; right: 20%; } /* Oben rechts */
.face-5 .dot3 { top: 50%; left: 50%; transform: translate(-50%, -50%); } /* Mitte */
.face-5 .dot4 { bottom: 20%; left: 20%; } /* Unten links */
.face-5 .dot5 { bottom: 20%; right: 20%; } /* Unten rechts */
/* Seite 6: Sechs (zwei Reihen zu je drei) */
.face-6 .dot1 { top: 20%; left: 25%; }
.face-6 .dot2 { top: 20%; right: 25%; }
.face-6 .dot3 { top: 50%; left: 25%; transform: translateY(-50%); }
.face-6 .dot4 { top: 50%; right: 25%; transform: translateY(-50%); }
.face-6 .dot5 { bottom: 20%; left: 25%; }
.face-6 .dot6 { bottom: 20%; right: 25%; }
.result {
color: white; font-size: 48px; margin: 20px 0; min-height: 60px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3); font-weight: bold;
}
.stats {
color: white; font-size: 18px; margin: 10px 0;
}
.history {
display: flex; gap: 10px; flex-wrap: wrap; justify-content: center;
max-width: 300px; margin: 15px 0;
}
.history-item {
width: 40px; height: 40px; background: white; border-radius: 8px;
display: flex; align-items: center; justify-content: center;
font-size: 20px; font-weight: bold; box-shadow: 0 3px 10px rgba(0,0,0,0.3);
}
.controls { display: flex; gap: 15px; margin-top: 20px; }
.btn {
background: white; border: none; border-radius: 10px; padding: 15px 40px;
font-size: 18px; cursor: pointer; font-family: inherit; font-weight: bold;
}
.btn-roll { background: #4ade80; color: white; }
.btn-roll:hover { transform: scale(1.05); }
.btn-roll:active { transform: scale(0.95); }
.btn-back { background: #ff6b6b; color: white; text-decoration: none; padding: 15px 25px; }
.space-hint { color: #ccc; margin-top: 10px; font-size: 14px; }
</style>
</head>
<body>
<h1>🎲 Würfel</h1>
<div class="dice-container">
<div class="dice" id="dice" onclick="roll()">
<!-- Seite 1: 1 Punkt -->
<div class="face face-1">
<div class="dot dot1"></div>
</div>
<!-- Seite 2: 2 Punkte -->
<div class="face face-2">
<div class="dot dot1"></div>
<div class="dot dot2"></div>
</div>
<!-- Seite 3: 3 Punkte -->
<div class="face face-3">
<div class="dot dot1"></div>
<div class="dot dot2"></div>
<div class="dot dot3"></div>
</div>
<!-- Seite 4: 4 Punkte -->
<div class="face face-4">
<div class="dot dot1"></div>
<div class="dot dot2"></div>
<div class="dot dot3"></div>
<div class="dot dot4"></div>
</div>
<!-- Seite 5: 5 Punkte (Vier Ecken + Mitte) -->
<div class="face face-5">
<div class="dot dot1"></div>
<div class="dot dot2"></div>
<div class="dot dot3"></div>
<div class="dot dot4"></div>
<div class="dot dot5"></div>
</div>
<!-- Seite 6: 6 Punkte -->
<div class="face face-6">
<div class="dot dot1"></div>
<div class="dot dot2"></div>
<div class="dot dot3"></div>
<div class="dot dot4"></div>
<div class="dot dot5"></div>
<div class="dot dot6"></div>
</div>
</div>
</div>
<div class="result" id="result">Klicke oder drücke Leertaste!</div>
<div class="stats">Würfe: <span id="rolls">0</span> | Summe: <span id="total">0</span></div>
<div class="history" id="history"></div>
<div class="controls">
<button class="btn btn-roll" id="rollBtn" onclick="roll()">🎲 Würfeln</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<p class="space-hint">💡 Tipp: Du kannst auch die LEERTASTE drücken!</p>
<script>
let rolling = false;
let rollCount = 0;
let total = 0;
const rotations = {
1: 'rotateX(0deg) rotateY(0deg)',
2: 'rotateX(0deg) rotateY(-90deg)',
3: 'rotateX(0deg) rotateY(180deg)',
4: 'rotateX(0deg) rotateY(90deg)',
5: 'rotateX(-90deg) rotateY(0deg)',
6: 'rotateX(90deg) rotateY(0deg)'
};
const dice = document.getElementById('dice');
const result = document.getElementById('result');
const rollBtn = document.getElementById('rollBtn');
function roll() {
if (rolling) return;
rolling = true;
rollBtn.disabled = true;
// Animation
dice.classList.add('rolling');
result.textContent = '🎲...';
// Zufällige Zahl
const roll = Math.floor(Math.random() * 6) + 1;
setTimeout(() => {
dice.classList.remove('rolling');
dice.style.transform = rotations[roll];
// Ergebnis anzeigen
result.innerHTML = `<span style="font-size:60px">${roll}</span>`;
// Statistik
rollCount++;
total += roll;
document.getElementById('rolls').textContent = rollCount;
document.getElementById('total').textContent = total;
// Historie
const history = document.getElementById('history');
const item = document.createElement('div');
item.className = 'history-item';
item.textContent = roll;
// Farben: Grün für 5-6, Gelb für 3-4, Rot für 1-2
if (roll >= 5) item.style.background = '#86efac';
else if (roll >= 3) item.style.background = '#feca57';
else item.style.background = '#fca5a5';
history.insertBefore(item, history.firstChild);
if (history.children.length > 10) {
history.removeChild(history.lastChild);
}
// TTS
if ('speechSynthesis' in window) {
const utterance = new SpeechSynthesisUtterance(roll.toString());
utterance.lang = 'de-DE';
speechSynthesis.speak(utterance);
}
rolling = false;
rollBtn.disabled = false;
}, 1000);
}
// Leertaste
document.addEventListener('keydown', (e) => {
if (e.code === 'Space') {
e.preventDefault();
roll();
}
});
</script>
</body>
</html>
+223
View File
@@ -0,0 +1,223 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Wie viele? - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 20px;
}
h1 { color: white; margin-bottom: 10px; }
.question { color: white; font-size: 22px; margin: 10px 0; text-align: center; }
.objects-area {
background: white; border-radius: 20px; padding: 30px; margin: 15px 0;
min-height: 200px; min-width: 300px; display: flex; flex-wrap: wrap;
justify-content: center; align-items: center; gap: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
}
.object-item { font-size: 50px; }
.answer-section { margin: 20px 0; text-align: center; }
.answer-input {
font-size: 48px; width: 100px; text-align: center; padding: 10px;
border: 3px solid white; border-radius: 15px; background: rgba(255,255,255,0.9);
font-family: inherit; font-weight: bold;
}
.pin-pad {
display: grid; grid-template-columns: repeat(3, 70px); gap: 10px;
justify-content: center; margin: 20px 0;
}
.pin-btn {
background: rgba(255,255,255,0.9); border: none; border-radius: 15px;
width: 70px; height: 70px; font-size: 28px; font-weight: bold;
cursor: pointer; font-family: inherit;
}
.pin-btn:active { transform: scale(0.95); background: white; }
.pin-btn.action { background: #4ade80; color: white; }
.pin-btn.clear { background: #ff6b6b; color: white; }
.feedback {
font-size: 36px; margin: 15px 0; min-height: 50px; font-weight: bold;
}
.feedback.correct { color: #4ade80; }
.feedback.wrong { color: #ff6b6b; }
.score { color: white; font-size: 20px; margin-bottom: 10px; }
.controls { display: flex; gap: 15px; margin-top: 15px; flex-wrap: wrap; justify-content: center; }
.btn {
background: white; border: none; border-radius: 10px; padding: 12px 25px;
font-size: 16px; cursor: pointer; font-family: inherit; font-weight: bold;
}
.btn-back { background: #ff6b6b; color: white; text-decoration: none; }
.range-select { margin-bottom: 15px; }
.range-select button {
background: rgba(255,255,255,0.2); border: 2px solid white; border-radius: 10px;
padding: 8px 15px; margin: 0 5px; color: white; font-size: 14px; cursor: pointer;
}
.range-select button.active { background: white; color: #667eea; }
</style>
</head>
<body>
<h1>🔢 Wie viele siehst du?</h1>
<div class="range-select">
<button onclick="setRange(5)" id="r5" class="active">Bis 5</button>
<button onclick="setRange(10)" id="r10">Bis 10</button>
<button onclick="setRange(20)" id="r20">Bis 20</button>
</div>
<div class="score">Richtig: <span id="correctCount">0</span> | Versuche: <span id="totalCount">0</span></div>
<div class="question" id="questionText">Wie viele 🍎 siehst du?</div>
<div class="objects-area" id="objectsArea"></div>
<div class="answer-section">
<input type="text" class="answer-input" id="answerInput" readonly placeholder="?">
</div>
<div class="feedback" id="feedback"></div>
<div class="pin-pad">
<button class="pin-btn" onclick="enterDigit(1)">1</button>
<button class="pin-btn" onclick="enterDigit(2)">2</button>
<button class="pin-btn" onclick="enterDigit(3)">3</button>
<button class="pin-btn" onclick="enterDigit(4)">4</button>
<button class="pin-btn" onclick="enterDigit(5)">5</button>
<button class="pin-btn" onclick="enterDigit(6)">6</button>
<button class="pin-btn" onclick="enterDigit(7)">7</button>
<button class="pin-btn" onclick="enterDigit(8)">8</button>
<button class="pin-btn" onclick="enterDigit(9)">9</button>
<button class="pin-btn clear" onclick="clearAnswer()"></button>
<button class="pin-btn" onclick="enterDigit(0)">0</button>
<button class="pin-btn action" onclick="checkAnswer()"></button>
</div>
<div class="controls">
<button class="btn" onclick="nextQuestion()">🔄 Nächste Aufgabe</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<script>
const objects = [
{ emoji: '🍎', name: 'Äpfel' },
{ emoji: '🚗', name: 'Autos' },
{ emoji: '⭐', name: 'Sterne' },
{ emoji: '🐱', name: 'Katzen' },
{ emoji: '🌸', name: 'Blumen' },
{ emoji: '🎈', name: 'Ballons' },
{ emoji: '🐶', name: 'Hunde' },
{ emoji: '🦋', name: 'Schmetterlinge' },
{ emoji: '🍕', name: 'Pizzas' },
{ emoji: '🚀', name: 'Raketen' },
{ emoji: '🐘', name: 'Elefanten' },
{ emoji: '🌈', name: 'Regenbögen' }
];
let currentCount = 0;
let currentObject = null;
let userAnswer = '';
let maxRange = 5;
let correctCount = 0;
let totalCount = 0;
const synth = window.speechSynthesis;
function setRange(range) {
maxRange = range;
document.querySelectorAll('.range-select button').forEach(btn => btn.classList.remove('active'));
document.getElementById('r' + range).classList.add('active');
nextQuestion();
}
function nextQuestion() {
// Zufällige Anzahl und Objekt
currentCount = Math.floor(Math.random() * maxRange) + 1;
currentObject = objects[Math.floor(Math.random() * objects.length)];
// Anzeigen
const area = document.getElementById('objectsArea');
area.innerHTML = '';
// Objekte verteilen
for (let i = 0; i < currentCount; i++) {
const span = document.createElement('span');
span.className = 'object-item';
span.textContent = currentObject.emoji;
span.style.animationDelay = (i * 0.1) + 's';
area.appendChild(span);
}
// Frage setzen
document.getElementById('questionText').textContent =
`Wie viele ${currentObject.name} siehst du?`;
// Zurücksetzen
userAnswer = '';
document.getElementById('answerInput').value = '';
document.getElementById('feedback').textContent = '';
document.getElementById('feedback').className = 'feedback';
// Vorlesen
speak(`Wie viele ${currentObject.name} siehst du?`);
}
function enterDigit(digit) {
if (userAnswer.length < 2) {
userAnswer += digit;
document.getElementById('answerInput').value = userAnswer;
}
}
function clearAnswer() {
userAnswer = '';
document.getElementById('answerInput').value = '';
}
function checkAnswer() {
if (!userAnswer) return;
const answer = parseInt(userAnswer);
totalCount++;
const feedback = document.getElementById('feedback');
if (answer === currentCount) {
correctCount++;
feedback.textContent = '🎉 Richtig! Sehr gut!';
feedback.className = 'feedback correct';
speak(`Richtig! Es sind ${currentCount} ${currentObject.name}. Sehr gut!`);
} else {
feedback.textContent = `❌ Fast! Es waren ${currentCount} ${currentObject.name}.`;
feedback.className = 'feedback wrong';
speak(`Fast! Es waren ${currentCount} ${currentObject.name}.`);
}
document.getElementById('correctCount').textContent = correctCount;
document.getElementById('totalCount').textContent = totalCount;
// Nach 2 Sekunden nächste Frage
setTimeout(nextQuestion, 2500);
}
function speak(text) {
if (!synth) return;
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'de-DE';
utterance.rate = 0.8;
utterance.pitch = 1.2;
synth.speak(utterance);
}
// Keyboard
document.addEventListener('keydown', (e) => {
if (e.key >= '0' && e.key <= '9') enterDigit(parseInt(e.key));
if (e.key === 'Backspace') clearAnswer();
if (e.key === 'Enter') checkAnswer();
});
// Init
nextQuestion();
</script>
</body>
</html>
+184
View File
@@ -0,0 +1,184 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Zahlen raten - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #a855f7 0%, #6366f1 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 20px;
}
h1 { color: white; margin-bottom: 15px; }
.game-area {
background: white; border-radius: 20px; padding: 30px; margin: 15px 0;
text-align: center; box-shadow: 0 10px 30px rgba(0,0,0,0.3); max-width: 400px; width: 100%;
}
.message { font-size: 24px; margin-bottom: 20px; color: #333; }
.attempts { color: #666; margin: 10px 0; }
.guess-display {
background: #f3f4f6; border-radius: 10px; padding: 15px; margin: 15px 0;
font-size: 36px; font-weight: bold; color: #333; min-height: 60px;
}
.pin-pad {
display: grid; grid-template-columns: repeat(3, 80px); gap: 10px;
justify-content: center; margin: 20px 0;
}
.pin-btn {
background: linear-gradient(135deg, #667eea, #764ba2); border: none; border-radius: 15px;
width: 80px; height: 80px; font-size: 28px; color: white; font-weight: bold;
cursor: pointer; box-shadow: 0 4px 10px rgba(0,0,0,0.2);
}
.pin-btn:active { transform: scale(0.95); }
.pin-btn.action { background: linear-gradient(135deg, #4ade80, #22c55e); }
.pin-btn.clear { background: linear-gradient(135deg, #ff6b6b, #f5576c); }
.controls { display: flex; gap: 15px; margin-top: 20px; flex-wrap: wrap; justify-content: center; }
.btn {
background: linear-gradient(135deg, #4ade80, #22c55e); border: none; border-radius: 10px;
padding: 12px 25px; font-size: 16px; cursor: pointer; font-family: inherit; font-weight: bold;
color: white;
}
.btn-back { background: #ff6b6b; text-decoration: none; }
.range-select { margin-bottom: 15px; }
.range-select button {
background: rgba(255,255,255,0.2); border: 2px solid white; border-radius: 10px;
padding: 8px 15px; margin: 0 5px; color: white; font-size: 14px; cursor: pointer;
}
.range-select button.active { background: white; color: #a855f7; }
.hint { color: #ff6b6b; font-size: 20px; margin-top: 10px; font-weight: bold; }
</style>
</head>
<body>
<h1>🤔 Zahlen raten</h1>
<div class="range-select">
<button onclick="setRange(10)" id="r10" class="active">1-10</button>
<button onclick="setRange(20)" id="r20">1-20</button>
<button onclick="setRange(50)" id="r50">1-50</button>
<button onclick="setRange(100)" id="r100">1-100</button>
</div>
<div class="game-area">
<div class="message" id="message">Ich denke an eine Zahl...</div>
<div class="attempts" id="attempts">Versuch 1</div>
<div class="guess-display" id="guessDisplay">?</div>
<div class="hint" id="hint"></div>
<div class="pin-pad">
<button class="pin-btn" onclick="enterDigit(1)">1</button>
<button class="pin-btn" onclick="enterDigit(2)">2</button>
<button class="pin-btn" onclick="enterDigit(3)">3</button>
<button class="pin-btn" onclick="enterDigit(4)">4</button>
<button class="pin-btn" onclick="enterDigit(5)">5</button>
<button class="pin-btn" onclick="enterDigit(6)">6</button>
<button class="pin-btn" onclick="enterDigit(7)">7</button>
<button class="pin-btn" onclick="enterDigit(8)">8</button>
<button class="pin-btn" onclick="enterDigit(9)">9</button>
<button class="pin-btn clear" onclick="clearGuess()"></button>
<button class="pin-btn" onclick="enterDigit(0)">0</button>
<button class="pin-btn action" onclick="checkGuess()"></button>
</div>
</div>
<div class="controls">
<button class="btn" onclick="initGame()">🔄 Neues Spiel</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<script>
let targetNumber = 0;
let currentGuess = '';
let attempts = 0;
let maxRange = 10;
const synth = window.speechSynthesis;
function setRange(range) {
maxRange = range;
document.querySelectorAll('.range-select button').forEach(btn => btn.classList.remove('active'));
document.getElementById('r' + range).classList.add('active');
initGame();
}
function initGame() {
targetNumber = Math.floor(Math.random() * maxRange) + 1;
currentGuess = '';
attempts = 0;
document.getElementById('message').textContent = `Ich denke an eine Zahl zwischen 1 und ${maxRange}...`;
document.getElementById('attempts').textContent = 'Versuch 1';
document.getElementById('guessDisplay').textContent = '?';
document.getElementById('hint').textContent = '';
speak(`Ich denke an eine Zahl zwischen 1 und ${maxRange}. Welche Zahl ist es?`);
}
function enterDigit(digit) {
if (currentGuess.length < 3) {
currentGuess += digit;
document.getElementById('guessDisplay').textContent = currentGuess;
speak(currentGuess);
}
}
function clearGuess() {
currentGuess = '';
document.getElementById('guessDisplay').textContent = '?';
}
function checkGuess() {
if (!currentGuess) return;
const guess = parseInt(currentGuess);
attempts++;
document.getElementById('attempts').textContent = `Versuch ${attempts + 1}`;
if (guess === targetNumber) {
document.getElementById('message').textContent = '🎉 Richtig! Super!';
document.getElementById('hint').textContent = `Du hast ${attempts} Versuche gebraucht.`;
document.getElementById('guessDisplay').style.background = '#4ade80';
speak(`Richtig! Die Zahl war ${targetNumber}. Du hast ${attempts} Versuche gebraucht.`);
setTimeout(() => {
document.getElementById('guessDisplay').style.background = '#f3f4f6';
}, 2000);
} else if (guess < targetNumber) {
document.getElementById('message').textContent = `${guess} ist zu klein!`;
document.getElementById('hint').textContent = '⬆️ Die Zahl ist größer!';
speak(`${guess} ist zu klein. Die Zahl ist größer.`);
currentGuess = '';
setTimeout(() => {
document.getElementById('guessDisplay').textContent = '?';
}, 1000);
} else {
document.getElementById('message').textContent = `${guess} ist zu groß!`;
document.getElementById('hint').textContent = '⬇️ Die Zahl ist kleiner!';
speak(`${guess} ist zu groß. Die Zahl ist kleiner.`);
currentGuess = '';
setTimeout(() => {
document.getElementById('guessDisplay').textContent = '?';
}, 1000);
}
}
function speak(text) {
if (!synth) return;
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'de-DE';
utterance.rate = 0.8;
utterance.pitch = 1.1;
synth.speak(utterance);
}
// Keyboard support
document.addEventListener('keydown', (e) => {
if (e.key >= '0' && e.key <= '9') enterDigit(parseInt(e.key));
if (e.key === 'Backspace') clearGuess();
if (e.key === 'Enter') checkGuess();
});
// Init
initGame();
</script>
</body>
</html>
+272
View File
@@ -0,0 +1,272 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Zielscheibe - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 20px; overflow: hidden;
}
h1 { color: white; margin-bottom: 10px; font-size: 26px; }
.score-board {
display: flex; gap: 30px; margin-bottom: 15px; color: white; font-size: 18px;
}
.score-item { text-align: center; }
.score-value { font-size: 32px; font-weight: bold; color: #feca57; }
.game-container {
position: relative; width: 350px; height: 350px;
background: rgba(255,255,255,0.1); border-radius: 20px;
overflow: hidden; cursor: crosshair;
}
.target {
position: absolute; border-radius: 50%; cursor: pointer;
animation: appear 0.3s ease-out;
}
@keyframes appear {
from { transform: scale(0); }
to { transform: scale(1); }
}
.target.hit { animation: hit 0.3s ease-out forwards; }
@keyframes hit {
to { transform: scale(1.5); opacity: 0; }
}
/* Ringe der Zielscheibe */
.ring-1 { width: 60px; height: 60px; background: radial-gradient(circle, #ff6b6b 30%, transparent 30%); }
.ring-2 { width: 80px; height: 80px; background: radial-gradient(circle, #ff6b6b 23%, #feca57 23%, #feca57 38%, transparent 38%); }
.ring-3 { width: 100px; height: 100px; background: radial-gradient(circle, #ff6b6b 18%, #feca57 18%, #feca57 30%, #4ade80 30%, #4ade80 45%, transparent 45%); }
.ring-4 { width: 120px; height: 120px; background: radial-gradient(circle, #ff6b6b 15%, #feca57 15%, #feca57 25%, #4ade80 25%, #4ade80 38%, #3b82f6 38%, #3b82f6 50%, transparent 50%); }
.ring-5 { width: 140px; height: 140px; background: radial-gradient(circle, #ff6b6b 13%, #feca57 13%, #feca57 21%, #4ade80 21%, #4ade80 33%, #3b82f6 33%, #3b82f6 43%, #fff 43%, #fff 50%, transparent 50%); }
.hit-text {
position: absolute; font-weight: bold; font-size: 24px;
animation: floatUp 1s ease-out forwards; pointer-events: none;
}
@keyframes floatUp {
0% { transform: translateY(0) scale(1); opacity: 1; }
100% { transform: translateY(-50px) scale(1.5); opacity: 0; }
}
.hit-10 { color: #ff6b6b; }
.hit-8 { color: #feca57; }
.hit-6 { color: #4ade80; }
.hit-4 { color: #3b82f6; }
.hit-2 { color: #a0aec0; }
.timer { color: white; font-size: 48px; margin: 10px 0; font-weight: bold; }
.controls { display: flex; gap: 15px; margin-top: 15px; }
.btn {
background: white; border: none; border-radius: 10px; padding: 12px 25px;
font-size: 16px; cursor: pointer; font-family: inherit; font-weight: bold;
}
.btn-start { background: #4ade80; color: white; }
.btn-back { background: #ff6b6b; color: white; text-decoration: none; }
.game-over {
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: white; padding: 30px; border-radius: 20px; text-align: center;
display: none; z-index: 100;
}
.game-over h2 { color: #333; margin-bottom: 15px; }
.final-score { font-size: 48px; color: #4ade80; font-weight: bold; }
</style>
</head>
<body>
<h1>🎯 Zielscheibe</h1>
<div class="score-board">
<div class="score-item">
<div>Punkte</div>
<div class="score-value" id="score">0</div>
</div>
<div class="score-item">
<div>Treffer</div>
<div class="score-value" id="hits">0</div>
</div>
</div>
<div class="timer" id="timer">30</div>
<div class="game-container" id="gameContainer">
<div class="game-over" id="gameOver">
<h2>🎉 Zeit abgelaufen!</h2>
<div class="final-score" id="finalScore">0</div>
<div style="margin-top: 15px; color: #666">Punkte</div>
<button class="btn btn-start" onclick="startGame()" style="margin-top: 20px">🔄 Nochmal</button>
</div>
</div>
<div class="controls">
<button class="btn btn-start" id="startBtn" onclick="startGame()">▶️ Start</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<script>
let score = 0;
let hits = 0;
let timeLeft = 30;
let gameActive = false;
let timerInterval = null;
let targetInterval = null;
let highscore = localStorage.getItem('zielscheibe_highscore') || 0;
const container = document.getElementById('gameContainer');
const scoreEl = document.getElementById('score');
const hitsEl = document.getElementById('hits');
const timerEl = document.getElementById('timer');
const gameOverEl = document.getElementById('gameOver');
const finalScoreEl = document.getElementById('finalScore');
function startGame() {
score = 0;
hits = 0;
timeLeft = 30;
gameActive = true;
scoreEl.textContent = '0';
hitsEl.textContent = '0';
timerEl.textContent = '30';
gameOverEl.style.display = 'none';
document.getElementById('startBtn').style.display = 'none';
// Container leeren
container.innerHTML = '';
container.appendChild(gameOverEl);
// Erste Zielscheibe
spawnTarget();
// Timer
timerInterval = setInterval(() => {
timeLeft--;
timerEl.textContent = timeLeft;
if (timeLeft <= 10) {
timerEl.style.color = '#ff6b6b';
}
if (timeLeft <= 0) {
endGame();
}
}, 1000);
// Neue Zielscheiben automatisch
targetInterval = setInterval(() => {
if (gameActive && document.querySelectorAll('.target').length < 3) {
spawnTarget();
}
}, 1500);
}
function spawnTarget() {
const target = document.createElement('div');
const ringSize = Math.floor(Math.random() * 5) + 1;
target.className = 'target ring-' + ringSize;
// Zufällige Position (nicht zu nah am Rand)
const maxX = container.offsetWidth - 150;
const maxY = container.offsetHeight - 150;
const x = 20 + Math.random() * maxX;
const y = 20 + Math.random() * maxY;
target.style.left = x + 'px';
target.style.top = y + 'px';
// Punkte basierend auf Ring
const points = ringSize === 1 ? 10 : ringSize === 2 ? 8 : ringSize === 3 ? 6 : ringSize === 4 ? 4 : 2;
target.onclick = function(e) {
e.stopPropagation();
if (!gameActive) return;
hitTarget(target, points, e.clientX, e.clientY);
};
// Automatisch entfernen nach 3 Sekunden
setTimeout(() => {
if (target.parentNode) {
target.remove();
}
}, 3000);
container.appendChild(target);
}
function hitTarget(target, points, x, y) {
score += points;
hits++;
scoreEl.textContent = score;
hitsEl.textContent = hits;
// Hit-Text anzeigen
const hitText = document.createElement('div');
hitText.className = 'hit-text hit-' + points;
hitText.textContent = '+' + points;
hitText.style.left = (target.offsetLeft + target.offsetWidth/2 - 20) + 'px';
hitText.style.top = target.offsetTop + 'px';
container.appendChild(hitText);
setTimeout(() => hitText.remove(), 1000);
// Animation
target.classList.add('hit');
// Sound via TTS
if ('speechSynthesis' in window && points >= 8) {
const utterance = new SpeechSynthesisUtterance(points + ' Punkte');
utterance.lang = 'de-DE';
utterance.rate = 1.2;
speechSynthesis.speak(utterance);
}
setTimeout(() => {
target.remove();
// Neue Zielscheibe sofort
if (gameActive) spawnTarget();
}, 300);
}
function endGame() {
gameActive = false;
clearInterval(timerInterval);
clearInterval(targetInterval);
finalScoreEl.textContent = score;
gameOverEl.style.display = 'block';
timerEl.style.color = 'white';
// Highscore
if (score > highscore) {
highscore = score;
localStorage.setItem('zielscheibe_highscore', highscore);
finalScoreEl.innerHTML = score + '<br><small style="font-size:20px">🏆 Neuer Rekord!</small>';
}
if ('speechSynthesis' in window) {
const utterance = new SpeechSynthesisUtterance('Spiel vorbei. Du hast ' + score + ' Punkte erreicht.');
utterance.lang = 'de-DE';
speechSynthesis.speak(utterance);
}
}
// Verfehlte Klicks = -1 Punkt
container.addEventListener('click', (e) => {
if (!gameActive || e.target.classList.contains('target')) return;
score = Math.max(0, score - 1);
scoreEl.textContent = score;
// Visuelles Feedback
const missText = document.createElement('div');
missText.className = 'hit-text';
missText.style.color = '#ff6b6b';
missText.textContent = '-1';
missText.style.left = (e.offsetX - 10) + 'px';
missText.style.top = (e.offsetY - 20) + 'px';
container.appendChild(missText);
setTimeout(() => missText.remove(), 800);
});
</script>
</body>
</html>