Files
2026-04-26 09:44:19 +02:00

442 lines
16 KiB
HTML
Raw Permalink 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>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>