1
0

Neu: Minesweeper

This commit is contained in:
Akamaru 2025-03-28 12:08:53 +01:00
parent b5bac3f7d8
commit 477bac5f8c
4 changed files with 732 additions and 1 deletions

@ -263,6 +263,10 @@
<h2 class="tool-title">Banana Runner</h2>
<p class="tool-description">Endlos-Runner Spiel mit einer springenden Banane.</p>
</a>
<a href="https://tools.ponywave.de/minesweeper/" class="tool-bubble">
<h2 class="tool-title">Minesweeper</h2>
<p class="tool-description">Klassisches Minesweeper mit verschiedenen Schwierigkeitsgraden.</p>
</a>
</div>
</div>

BIN
minesweeper/icon.png Normal file

Binary file not shown.

After

(image error) Size: 92 KiB

726
minesweeper/index.html Normal file

@ -0,0 +1,726 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Minesweeper | PonyWave Tools</title>
<meta property="og:title" content="Minesweeper | PonyWave Tools">
<meta property="og:description" content="Klassisches Minesweeper-Spiel mit verschiedenen Schwierigkeitsgraden">
<meta property="og:type" content="website">
<meta property="og:url" content="https://tools.ponywave.de/minesweeper">
<meta property="og:image" content="https://tools.ponywave.de/minesweeper/icon.png">
<!-- Favicons -->
<link rel="icon" type="image/png" href="https://tools.ponywave.de/minesweeper/icon.png">
<link rel="shortcut icon" href="https://tools.ponywave.de/minesweeper/icon.png">
<link rel="apple-touch-icon" href="https://tools.ponywave.de/minesweeper/icon.png">
<link rel="icon" sizes="192x192" href="https://tools.ponywave.de/minesweeper/icon.png">
<!-- Umami Tracking -->
<script defer src="https://stats.ponywave.de/script" data-website-id="9ef713d2-adb9-4906-9df5-708d8a8b9131" data-tag="minesweeper"></script>
<style>
:root {
--primary-color: #7F006E;
--secondary-color: #FF7FED;
--bg-gradient: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
--text-color: #2c3e50;
--cell-size: 30px;
--board-bg: #c0c0c0;
--cell-bg: #c0c0c0;
--cell-border: #808080;
--cell-revealed: #d9d9d9;
--font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
--shadow-color: rgba(0, 0, 0, 0.1);
--footer-color: rgba(255, 255, 255, 0.8);
--button-bg: #f0f0f0;
--button-hover: #e0e0e0;
--button-active: #d0d0d0;
}
.dark-mode {
--bg-gradient: linear-gradient(135deg, #4A0040 0%, #CC65B5 100%);
--text-color: #ffffff;
--board-bg: #444444;
--cell-bg: #555555;
--cell-border: #666666;
--cell-revealed: #333333;
--button-bg: #555555;
--button-hover: #666666;
--button-active: #777777;
--shadow-color: rgba(0, 0, 0, 0.3);
--footer-color: rgba(0, 0, 0, 0.8);
}
body {
font-family: var(--font-family);
background-color: var(--board-bg);
background-image: none;
color: var(--text-color);
margin: 0;
padding: 20px 20px 60px;
min-height: 100vh;
position: relative;
line-height: 1.6;
text-align: center;
}
.dark-mode body {
background-image: none;
background-color: #444444;
}
.theme-toggle {
position: fixed;
top: 20px;
right: 20px;
background: var(--button-bg);
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
cursor: pointer;
box-shadow: 0 2px 4px var(--shadow-color);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2em;
transition: all 0.3s ease;
z-index: 10;
}
.theme-toggle:hover {
transform: scale(1.1);
}
h1 {
margin: 20px 0;
color: var(--text-color);
text-shadow: 2px 2px 4px var(--shadow-color);
}
.game-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin: 20px auto;
max-width: 800px;
background: rgba(255, 255, 255, 0.9);
padding: 20px;
border-radius: 15px;
box-shadow: 0 4px 6px var(--shadow-color);
backdrop-filter: blur(5px);
overflow-x: hidden;
width: 90%;
}
.dark-mode .game-container {
background: rgba(45, 45, 45, 0.9);
}
.game-header {
display: flex;
width: 100%;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.counter {
background: #000;
color: #f00;
font-family: 'Digital', monospace;
padding: 5px 10px;
border-radius: 3px;
font-size: 1.5em;
min-width: 80px;
text-align: center;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
}
.smiley {
width: 40px;
height: 40px;
background: var(--button-bg);
border: 2px outset var(--cell-border);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5em;
cursor: pointer;
user-select: none;
}
.smiley:active {
border-style: inset;
}
.difficulty {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.difficulty button {
padding: 8px 15px;
background: var(--button-bg);
border: 1px solid var(--cell-border);
border-radius: 5px;
cursor: pointer;
transition: all 0.2s;
font-family: var(--font-family);
}
.difficulty button:hover {
background: var(--button-hover);
}
.difficulty button.active {
background: var(--primary-color);
color: white;
}
.minesweeper-board {
display: grid;
background: var(--board-bg);
border: 3px solid var(--cell-border);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
margin: 0 auto;
user-select: none;
max-width: 100%;
}
.cell {
width: var(--cell-size);
height: var(--cell-size);
background: var(--cell-bg);
border: 2px outset var(--cell-border);
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 1.1em;
cursor: pointer;
transition: background-color 0.1s;
}
.cell.revealed {
background: var(--cell-revealed);
border: 1px solid var(--cell-border);
}
.cell.flagged::before {
content: "🚩";
}
.cell.mine-clicked {
background: red;
}
.cell.mine::before {
content: "💣";
}
.cell.number-1 { color: blue; }
.cell.number-2 { color: green; }
.cell.number-3 { color: red; }
.cell.number-4 { color: darkblue; }
.cell.number-5 { color: brown; }
.cell.number-6 { color: teal; }
.cell.number-7 { color: black; }
.cell.number-8 { color: gray; }
.instructions {
margin: 20px auto;
max-width: 600px;
text-align: left;
padding: 15px;
background: rgba(255, 255, 255, 0.8);
border-radius: 10px;
box-shadow: 0 2px 5px var(--shadow-color);
}
.dark-mode .instructions {
background: rgba(45, 45, 45, 0.8);
}
footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 15px;
text-align: center;
font-size: 0.9em;
background: var(--footer-color);
backdrop-filter: blur(5px);
z-index: 10;
}
footer a {
color: var(--primary-color);
text-decoration: none;
font-weight: 500;
transition: opacity 0.3s ease;
}
footer a:hover {
opacity: 0.8;
text-decoration: underline;
}
@media (max-width: 600px) {
:root {
--cell-size: 25px;
}
.game-container {
padding: 10px;
}
.counter {
font-size: 1.2em;
min-width: 60px;
}
.smiley {
width: 35px;
height: 35px;
}
.difficulty button {
padding: 6px 10px;
font-size: 0.9em;
}
}
@media (max-width: 400px) {
:root {
--cell-size: 20px;
}
}
</style>
</head>
<body>
<button class="theme-toggle" onclick="toggleTheme()">🌙</button>
<h1>Minesweeper</h1>
<div class="game-container">
<div class="game-header">
<div class="counter" id="mines-counter">000</div>
<div class="smiley" id="smiley">😊</div>
<div class="counter" id="timer">000</div>
</div>
<div class="difficulty">
<button data-level="beginner" class="active">Anfänger (9x9)</button>
<button data-level="intermediate">Fortgeschritten (16x16)</button>
<button data-level="expert">Experte (30x16)</button>
</div>
<div class="minesweeper-board" id="board"></div>
</div>
<div class="instructions">
<h3>Anleitung:</h3>
<p><strong>Ziel des Spiels:</strong> Finde alle Minen auf dem Spielfeld, ohne eine zu aktivieren.</p>
<p><strong>Steuerung:</strong></p>
<ul>
<li><strong>Linksklick:</strong> Feld aufdecken</li>
<li><strong>Rechtsklick:</strong> Flagge setzen/entfernen</li>
<li><strong>Doppelklick auf aufgedecktes Feld:</strong> Umgebende Felder aufdecken, wenn entsprechende Anzahl Flaggen gesetzt ist</li>
</ul>
<p><strong>Zahlen:</strong> Zeigen an, wie viele Minen in den umliegenden 8 Feldern versteckt sind.</p>
</div>
<footer>
<div>
<a href="https://tools.ponywave.de/">Zurück zur Startseite</a> |
&copy; <span id="currentYear"></span> Akamaru |
Made with ❤️ by Claude
</div>
</footer>
<script>
// Spielkonfiguration
const levels = {
beginner: { rows: 9, cols: 9, mines: 10 },
intermediate: { rows: 16, cols: 16, mines: 40 },
expert: { rows: 16, cols: 30, mines: 99 }
};
let currentLevel = 'beginner';
let boardData = [];
let gameOver = false;
let gameStarted = false;
let timeInterval;
let startTime;
let flagCount = 0;
// DOM-Elemente
const board = document.getElementById('board');
const smiley = document.getElementById('smiley');
const minesCounter = document.getElementById('mines-counter');
const timerElement = document.getElementById('timer');
const difficultyButtons = document.querySelectorAll('.difficulty button');
// Event-Listener
difficultyButtons.forEach(button => {
button.addEventListener('click', (e) => {
currentLevel = e.target.dataset.level;
difficultyButtons.forEach(btn => btn.classList.remove('active'));
e.target.classList.add('active');
initGame();
});
});
smiley.addEventListener('click', initGame);
// Initialisieren des Spiels
function initGame() {
clearInterval(timeInterval);
gameOver = false;
gameStarted = false;
flagCount = 0;
smiley.textContent = '😊';
timerElement.textContent = '000';
const { rows, cols, mines } = levels[currentLevel];
// Brett erstellen
board.style.gridTemplateColumns = `repeat(${cols}, var(--cell-size))`;
board.style.gridTemplateRows = `repeat(${rows}, var(--cell-size))`;
board.innerHTML = '';
// Zellengröße anpassen im Experten-Modus
if (currentLevel === 'expert') {
const gameContainer = document.querySelector('.game-container');
const containerWidth = gameContainer.clientWidth - 40; // Container-Breite abzüglich Padding
const cellSize = Math.max(15, Math.floor(containerWidth / cols)); // Minimalgröße 15px
document.documentElement.style.setProperty('--cell-size', `${cellSize}px`);
} else {
// Zurücksetzen auf Standardwert für andere Modi
document.documentElement.style.setProperty('--cell-size', '30px');
}
// Minen-Counter aktualisieren
updateMinesCounter(mines);
// Spielbrett-Daten initialisieren
boardData = Array(rows).fill().map(() =>
Array(cols).fill().map(() => ({
isMine: false,
isRevealed: false,
isFlagged: false,
neighborMines: 0
}))
);
// Zellen erstellen
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
const cell = document.createElement('div');
cell.className = 'cell';
cell.dataset.row = row;
cell.dataset.col = col;
cell.addEventListener('click', handleCellClick);
cell.addEventListener('contextmenu', handleRightClick);
cell.addEventListener('dblclick', handleDoubleClick);
board.appendChild(cell);
}
}
}
// Minen platzieren
function placeMines(firstClickRow, firstClickCol) {
const { rows, cols, mines } = levels[currentLevel];
let minesPlaced = 0;
while (minesPlaced < mines) {
const row = Math.floor(Math.random() * rows);
const col = Math.floor(Math.random() * cols);
// Nicht auf die erste Klick-Position oder wo bereits eine Mine ist
if ((row !== firstClickRow || col !== firstClickCol) && !boardData[row][col].isMine) {
boardData[row][col].isMine = true;
minesPlaced++;
}
}
// Nachbarn-Minen-Zähler berechnen
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
if (!boardData[row][col].isMine) {
boardData[row][col].neighborMines = countNeighborMines(row, col);
}
}
}
}
// Nachbarn-Minen zählen
function countNeighborMines(row, col) {
const { rows, cols } = levels[currentLevel];
let count = 0;
for (let r = Math.max(0, row - 1); r <= Math.min(rows - 1, row + 1); r++) {
for (let c = Math.max(0, col - 1); c <= Math.min(cols - 1, col + 1); c++) {
if ((r !== row || c !== col) && boardData[r][c].isMine) {
count++;
}
}
}
return count;
}
// Zellen-Klick behandeln
function handleCellClick(e) {
if (gameOver) return;
const row = parseInt(e.target.dataset.row);
const col = parseInt(e.target.dataset.col);
// Erster Klick initialisiert das Spiel
if (!gameStarted) {
gameStarted = true;
startTimer();
placeMines(row, col);
}
// Nicht auf eine Flagge klicken
if (boardData[row][col].isFlagged) return;
// Bei Minen-Klick Game Over
if (boardData[row][col].isMine) {
revealMines();
gameOver = true;
clearInterval(timeInterval);
smiley.textContent = '😵';
e.target.classList.add('mine-clicked');
return;
}
// Zelle aufdecken
revealCell(row, col);
// Prüfen auf Sieg
checkWin();
}
// Rechtsklick behandeln (Flagge setzen)
function handleRightClick(e) {
e.preventDefault();
if (gameOver || !gameStarted) return;
const row = parseInt(e.target.dataset.row);
const col = parseInt(e.target.dataset.col);
// Nur nicht aufgedeckte Zellen flaggen
if (boardData[row][col].isRevealed) return;
const cell = e.target;
if (boardData[row][col].isFlagged) {
// Flagge entfernen
boardData[row][col].isFlagged = false;
cell.classList.remove('flagged');
flagCount--;
} else {
// Flagge setzen
boardData[row][col].isFlagged = true;
cell.classList.add('flagged');
flagCount++;
}
// Minen-Counter aktualisieren
updateMinesCounter(levels[currentLevel].mines - flagCount);
}
// Doppelklick behandeln (Nachbarn aufdecken)
function handleDoubleClick(e) {
if (gameOver || !gameStarted) return;
const row = parseInt(e.target.dataset.row);
const col = parseInt(e.target.dataset.col);
// Nur für aufgedeckte Zellen mit Zahlen
if (!boardData[row][col].isRevealed || boardData[row][col].neighborMines === 0) return;
const { rows, cols } = levels[currentLevel];
let flagCount = 0;
// Zählen der Flaggen in der Umgebung
for (let r = Math.max(0, row - 1); r <= Math.min(rows - 1, row + 1); r++) {
for (let c = Math.max(0, col - 1); c <= Math.min(cols - 1, col + 1); c++) {
if (boardData[r][c].isFlagged) {
flagCount++;
}
}
}
// Wenn die Anzahl der Flaggen mit der Zahl übereinstimmt, Nachbarn aufdecken
if (flagCount === boardData[row][col].neighborMines) {
for (let r = Math.max(0, row - 1); r <= Math.min(rows - 1, row + 1); r++) {
for (let c = Math.max(0, col - 1); c <= Math.min(cols - 1, col + 1); c++) {
if (!boardData[r][c].isRevealed && !boardData[r][c].isFlagged) {
// Simuliere einen Klick auf die Zelle
const cellElement = document.querySelector(`.cell[data-row="${r}"][data-col="${c}"]`);
if (cellElement) {
cellElement.click();
}
}
}
}
}
}
// Zelle aufdecken
function revealCell(row, col) {
const { rows, cols } = levels[currentLevel];
// Bereits aufgedeckt oder geflaggt überspringen
if (boardData[row][col].isRevealed || boardData[row][col].isFlagged) return;
// Zelle als aufgedeckt markieren
boardData[row][col].isRevealed = true;
// DOM aktualisieren
const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
cell.classList.add('revealed');
// Nachbarn-Anzahl anzeigen wenn > 0
if (boardData[row][col].neighborMines > 0) {
cell.textContent = boardData[row][col].neighborMines;
cell.classList.add(`number-${boardData[row][col].neighborMines}`);
} else {
// Bei 0 Nachbarn automatisch alle angrenzenden Zellen aufdecken
for (let r = Math.max(0, row - 1); r <= Math.min(rows - 1, row + 1); r++) {
for (let c = Math.max(0, col - 1); c <= Math.min(cols - 1, col + 1); c++) {
if (r !== row || c !== col) {
revealCell(r, c);
}
}
}
}
}
// Alle Minen aufdecken (bei Game Over)
function revealMines() {
const { rows, cols } = levels[currentLevel];
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
if (boardData[row][col].isMine) {
const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
cell.classList.add('revealed', 'mine');
} else if (boardData[row][col].isFlagged) {
// Falsch platzierte Flaggen markieren
const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
cell.classList.add('revealed');
cell.classList.add('wrong-flag');
cell.textContent = '❌';
}
}
}
}
// Prüfen, ob das Spiel gewonnen wurde
function checkWin() {
const { rows, cols, mines } = levels[currentLevel];
let revealedCount = 0;
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
if (boardData[row][col].isRevealed) {
revealedCount++;
}
}
}
// Gewonnen, wenn alle Nicht-Minen-Zellen aufgedeckt sind
if (revealedCount === (rows * cols - mines)) {
gameOver = true;
clearInterval(timeInterval);
smiley.textContent = '😎';
// Alle Minen automatisch flaggen
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
if (boardData[row][col].isMine && !boardData[row][col].isFlagged) {
boardData[row][col].isFlagged = true;
const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
cell.classList.add('flagged');
}
}
}
// Minen-Counter auf 0 setzen
updateMinesCounter(0);
}
}
// Timer starten
function startTimer() {
startTime = Date.now();
timeInterval = setInterval(() => {
const elapsedSeconds = Math.floor((Date.now() - startTime) / 1000);
// Maximale Anzeige: 999 Sekunden
const displayTime = Math.min(elapsedSeconds, 999);
timerElement.textContent = displayTime.toString().padStart(3, '0');
}, 1000);
}
// Minen-Counter aktualisieren
function updateMinesCounter(count) {
minesCounter.textContent = count.toString().padStart(3, '0');
}
// Dark Mode
function toggleTheme() {
document.body.classList.toggle('dark-mode');
const btn = document.querySelector('.theme-toggle');
btn.textContent = document.body.classList.contains('dark-mode') ? '☀️' : '🌙';
// Speichern der Präferenz
const isDark = document.body.classList.contains('dark-mode');
localStorage.setItem('theme', isDark ? 'dark' : 'light');
}
// Theme initialisieren
function initTheme() {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark' || (!savedTheme && prefersDark)) {
document.body.classList.add('dark-mode');
document.querySelector('.theme-toggle').textContent = '☀️';
}
// Jahr aktualisieren
document.getElementById('currentYear').textContent = new Date().getFullYear();
}
// Kontextmenü für rechte Maustaste verhindern
board.addEventListener('contextmenu', e => e.preventDefault());
// Beim Laden ausführen
initTheme();
initGame();
// Bei Resize Fenster die Zellengröße anpassen
window.addEventListener('resize', () => {
if (currentLevel === 'expert') {
const gameContainer = document.querySelector('.game-container');
const containerWidth = gameContainer.clientWidth - 40;
const cellSize = Math.max(15, Math.floor(containerWidth / levels.expert.cols)); // Minimalgröße 15px
document.documentElement.style.setProperty('--cell-size', `${cellSize}px`);
}
});
</script>
</body>
</html>

@ -23,4 +23,5 @@ https://tools.ponywave.de/emoji
https://tools.ponywave.de/banana_run
https://tools.ponywave.de/pokemon_quiz
https://tools.ponywave.de/gronkh_games
https://tools.ponywave.de/url_expander/
https://tools.ponywave.de/url_expander/
https://tools.ponywave.de/minesweeper/