Files
Kinderspiele/game-server/public/spiele/puzzle.html
T
2026-04-26 09:44:19 +02:00

305 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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>