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
+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>