Initial commit - Kinderspiele
This commit is contained in:
@@ -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>
|
||||
Reference in New Issue
Block a user