Play Here
https://promptjourneys.com/files/minesweeper.html
HTML, CSS, and Javascript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Minesweeper</title>
<style>
/* Add your CSS styles here */
.minesweeper-button {
width: 30px;
height: 30px;
border: 1px solid gray;
background: lightgray;
margin: 2px;
}
.minesweeper-button.revealed {
background: white;
}
</style>
</head>
<body><div id="minesweeper-root"></div>
<style>
.minesweeper-button {
width: 30px;
height: 30px;
border: 1px solid gray;
background: lightgray;
margin: 2px;
}
.minesweeper-button.revealed {
background: white;
}
</style>
<script>
const GRID_SIZE = 8;
const NUM_MINES = 10;
const createBoard = () => {
const board = [];
for (let i = 0; i < GRID_SIZE; i++) {
board.push(Array(GRID_SIZE).fill().map(() => ({ isMine: false, isRevealed: false, isFlagged: false, neighborMines: 0 })));
}
return board;
};
const placeMines = (board, safeX, safeY) => {
let minesPlaced = 0;
while (minesPlaced < NUM_MINES) {
const x = Math.floor(Math.random() * GRID_SIZE);
const y = Math.floor(Math.random() * GRID_SIZE);
if (!board[y][x].isMine && (x !== safeX || y !== safeY)) {
board[y][x].isMine = true;
minesPlaced++;
}
}
for (let y = 0; y < GRID_SIZE; y++) {
for (let x = 0; x < GRID_SIZE; x++) {
if (!board[y][x].isMine) {
board[y][x].neighborMines = countAdjacentMines(board, x, y);
}
}
}
};
const countAdjacentMines = (board, x, y) => {
let count = 0;
for (let dy = -1; dy <= 1; dy++) {
for (let dx = -1; dx <= 1; dx++) {
const ny = y + dy;
const nx = x + dx;
if (ny >= 0 && ny < GRID_SIZE && nx >= 0 && nx < GRID_SIZE && board[ny][nx].isMine) {
count++;
}
}
}
return count;
};
const revealCell = (board, x, y, firstClick, gameOver, gameWon) => {
if (gameOver || gameWon || board[y][x].isFlagged) return { board, gameOver, gameWon };
const newBoard = JSON.parse(JSON.stringify(board));
if (firstClick) {
placeMines(newBoard, x, y);
firstClick = false;
}
revealRecursive(newBoard, x, y);
if (newBoard[y][x].isMine) {
gameOver = true;
} else {
const { won } = checkWinCondition(newBoard);
gameWon = won;
}
return { board: newBoard, firstClick, gameOver, gameWon };
};
const revealRecursive = (board, x, y) => {
if (x < 0 || x >= GRID_SIZE || y < 0 || y >= GRID_SIZE || board[y][x].isRevealed) return;
board[y][x].isRevealed = true;
if (board[y][x].neighborMines === 0) {
for (let dy = -1; dy <= 1; dy++) {
for (let dx = -1; dx <= 1; dx++) {
revealRecursive(board, x + dx, y + dy);
}
}
}
};
const toggleFlag = (board, x, y, gameOver, gameWon) => {
if (gameOver || gameWon || board[y][x].isRevealed) return board;
const newBoard = JSON.parse(JSON.stringify(board));
newBoard[y][x].isFlagged = !newBoard[y][x].isFlagged;
return newBoard;
};
const checkWinCondition = (board) => {
const allNonMinesRevealed = board.every(row =>
row.every(cell => cell.isMine || cell.isRevealed)
);
return { won: allNonMinesRevealed };
};
const resetGame = () => {
return createBoard();
};
document.addEventListener('DOMContentLoaded', () => {
const root = document.getElementById('minesweeper-root');
let board = createBoard();
let gameOver = false;
let gameWon = false;
let firstClick = true;
const render = () => {
root.innerHTML = `
<div style="font-family: Arial, sans-serif; text-align: center; margin-top: 20px;">
<h1>Minesweeper</h1>
<div style="display: inline-block; border: 1px solid black; padding: 10px;">
${board.map((row, y) => `
<div style="display: flex;">
${row.map((cell, x) => `
<button
onclick="handleClick(${x}, ${y})"
oncontextmenu="handleRightClick(event, ${x}, ${y})"
class="minesweeper-button ${cell.isRevealed ? 'revealed' : ''}"
style="width: 30px; height: 30px; border: 1px solid gray; background: ${cell.isRevealed ? 'white' : 'lightgray'}; margin: 2px;"
>
${cell.isRevealed
? (cell.isMine ? '💣' : cell.neighborMines || '')
: cell.isFlagged ? '🚩' : ''}
</button>
`).join('')}
</div>
`).join('')}
</div>
${gameOver ? '<p>Game Over! You hit a mine.</p>' : ''}
${gameWon ? '<p>Congratulations! You won!</p>' : ''}
${(gameOver || gameWon) ? '<button onclick="handleReset()">New Game</button>' : ''}
</div>
`;
};
window.handleClick = (x, y) => {
const result = revealCell(board, x, y, firstClick, gameOver, gameWon);
board = result.board;
firstClick = result.firstClick;
gameOver = result.gameOver;
gameWon = result.gameWon;
render();
};
window.handleRightClick = (event, x, y) => {
event.preventDefault();
board = toggleFlag(board, x, y, gameOver, gameWon);
render();
};
window.handleReset = () => {
board = resetGame();
gameOver = false;
gameWon = false;
firstClick = true;
render();
};
render();
});
</script>
</body>
</html>
React Component
import React, { useState, useEffect } from 'react';
const GRID_SIZE = 8;
const NUM_MINES = 10;
const createBoard = () => {
const board = [];
for (let i = 0; i < GRID_SIZE; i++) {
board.push(Array(GRID_SIZE).fill().map(() => ({ isMine: false, isRevealed: false, isFlagged: false, neighborMines: 0 })));
}
return board;
};
const placeMines = (board, safeX, safeY) => {
let minesPlaced = 0;
while (minesPlaced < NUM_MINES) {
const x = Math.floor(Math.random() * GRID_SIZE);
const y = Math.floor(Math.random() * GRID_SIZE);
if (!board[y][x].isMine && (x !== safeX || y !== safeY)) {
board[y][x].isMine = true;
minesPlaced++;
}
}
for (let y = 0; y < GRID_SIZE; y++) {
for (let x = 0; x < GRID_SIZE; x++) {
if (!board[y][x].isMine) {
board[y][x].neighborMines = countAdjacentMines(board, x, y);
}
}
}
};
const countAdjacentMines = (board, x, y) => {
let count = 0;
for (let dy = -1; dy <= 1; dy++) {
for (let dx = -1; dx <= 1; dx++) {
const ny = y + dy;
const nx = x + dx;
if (ny >= 0 && ny < GRID_SIZE && nx >= 0 && nx < GRID_SIZE && board[ny][nx].isMine) {
count++;
}
}
}
return count;
};
const Minesweeper = () => {
const [board, setBoard] = useState(createBoard);
const [gameOver, setGameOver] = useState(false);
const [gameWon, setGameWon] = useState(false);
const [firstClick, setFirstClick] = useState(true);
const revealCell = (x, y) => {
if (gameOver || gameWon || board[y][x].isFlagged) return;
const newBoard = JSON.parse(JSON.stringify(board));
if (firstClick) {
placeMines(newBoard, x, y);
setFirstClick(false);
}
revealRecursive(newBoard, x, y);
if (newBoard[y][x].isMine) {
setGameOver(true);
} else {
checkWinCondition(newBoard);
}
setBoard(newBoard);
};
const revealRecursive = (board, x, y) => {
if (x < 0 || x >= GRID_SIZE || y < 0 || y >= GRID_SIZE || board[y][x].isRevealed) return;
board[y][x].isRevealed = true;
if (board[y][x].neighborMines === 0) {
for (let dy = -1; dy <= 1; dy++) {
for (let dx = -1; dx <= 1; dx++) {
revealRecursive(board, x + dx, y + dy);
}
}
}
};
const toggleFlag = (e, x, y) => {
e.preventDefault();
if (gameOver || gameWon || board[y][x].isRevealed) return;
const newBoard = JSON.parse(JSON.stringify(board));
newBoard[y][x].isFlagged = !newBoard[y][x].isFlagged;
setBoard(newBoard);
};
const checkWinCondition = (board) => {
const allNonMinesRevealed = board.every(row =>
row.every(cell => cell.isMine || cell.isRevealed)
);
if (allNonMinesRevealed) {
setGameWon(true);
}
};
const resetGame = () => {
setBoard(createBoard());
setGameOver(false);
setGameWon(false);
setFirstClick(true);
};
return (
<div style={{ fontFamily: 'Arial, sans-serif', textAlign: 'center', marginTop: '20px' }}>
<h1>Minesweeper</h1>
<div style={{ display: 'inline-block', border: '1px solid black', padding: '10px' }}>
{board.map((row, y) => (
<div key={y} style={{ display: 'flex' }}>
{row.map((cell, x) => (
<button
key={x}
onClick={() => revealCell(x, y)}
onContextMenu={(e) => toggleFlag(e, x, y)}
disabled={gameOver || gameWon}
style={{
width: '30px',
height: '30px',
border: '1px solid gray',
background: cell.isRevealed ? 'white' : 'lightgray',
margin: '2px',
}}
>
{cell.isRevealed
? (cell.isMine ? '💣' : cell.neighborMines || '')
: cell.isFlagged ? '🚩' : ''}
</button>
))}
</div>
))}
</div>
{gameOver && <p>Game Over! You hit a mine.</p>}
{gameWon && <p>Congratulations! You won!</p>}
{(gameOver || gameWon) && (
<button onClick={resetGame}>New Game</button>
)}
</div>
);
};
export default Minesweeper;