Code: Select all
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom Minesweeper</title>
<style>
* {
box-sizing: border-box;
font-family: 'Courier New', monospace;
}
body {
background-color: #f0f0f0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
padding: 20px;
}
.game-container {
background-color: #c0c0c0;
border: 2px solid #808080;
border-radius: 5px;
padding: 15px;
box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.2);
max-width: 100%;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding: 5px 10px;
background-color: #c0c0c0;
border: 2px inset #c0c0c0;
}
.controls {
display: flex;
gap: 10px;
margin-bottom: 15px;
flex-wrap: wrap;
}
.controls input {
width: 60px;
padding: 5px;
}
button {
background-color: #c0c0c0;
border: 2px outset #c0c0c0;
padding: 5px 10px;
cursor: pointer;
font-weight: bold;
}
button:hover {
background-color: #d0d0d0;
}
.game-board {
display: grid;
gap: 1px;
background-color: #808080;
border: 2px inset #c0c0c0;
margin-bottom: 15px;
overflow: auto;
max-width: 100%;
}
.cell {
width: 30px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
background-color: #c0c0c0;
border: 2px outset #c0c0c0;
font-weight: bold;
cursor: pointer;
user-select: none;
}
.cell.revealed {
border: 1px solid #808080;
background-color: #e0e0e0;
}
.cell.mine {
background-color: #ff0000;
}
.cell.flagged::after {
content: "🚩";
}
.status {
text-align: center;
font-weight: bold;
margin-top: 10px;
height: 24px;
}
.pixel-font {
font-family: 'Courier New', monospace;
font-weight: bold;
}
</style>
</head>
<body>
<div class="game-container">
<div class="header">
<div class="controls">
<div>
<label>Width:</label>
<input type="number" id="width" min="5" max="30" value="10">
</div>
<div>
<label>Height:</label>
<input type="number" id="height" min="5" max="30" value="10">
</div>
<div>
<label>Mines:</label>
<input type="number" id="mines" min="1" max="100" value="15">
</div>
<button id="new-game">New Game</button>
</div>
</div>
<div class="game-board" id="board"></div>
<div class="status" id="status">Click to start</div>
</div>
<script>
// The provided rulekeys array
const rulekeys = [
"", "c", "e", "a", "c", "c", "a", "i",
"e", "k", "e", "j", "a", "n", "a", "a",
"c", "n", "k", "q", "c", "c", "n", "n",
"a", "q", "j", "w", "i", "n", "a", "a",
"e", "k", "i", "r", "k", "y", "r", "t",
"e", "k", "e", "j", "j", "k", "r", "n",
"a", "q", "r", "z", "n", "y", "i", "r",
"a", "q", "r", "q", "a", "j", "i", "a",
"c", "c", "k", "n", "n", "c", "q", "n",
"k", "y", "k", "k", "q", "y", "q", "j",
"c", "c", "y", "y", "c", "c", "y", "e",
"n", "y", "k", "k", "n", "e", "j", "e",
"a", "n", "r", "i", "q", "y", "z", "r",
"j", "k", "j", "y", "w", "k", "q", "k",
"i", "n", "t", "r", "n", "e", "r", "i",
"a", "j", "n", "k", "a", "e", "a", "e",
"e", "a", "e", "a", "k", "n", "j", "a",
"i", "r", "e", "r", "r", "i", "r", "i",
"k", "q", "k", "q", "y", "y", "k", "j",
"r", "z", "j", "q", "t", "r", "n", "a",
"e", "j", "e", "r", "k", "k", "j", "n",
"e", "j", "e", "c", "j", "y", "c", "c",
"j", "w", "j", "q", "k", "k", "y", "k",
"r", "q", "c", "n", "n", "k", "c", "c",
"a", "i", "j", "a", "q", "n", "w", "a",
"r", "t", "j", "n", "z", "r", "q", "a",
"n", "n", "k", "j", "y", "e", "k", "e",
"i", "r", "y", "k", "r", "i", "k", "e",
"a", "a", "r", "i", "q", "j", "q", "a",
"r", "n", "c", "c", "q", "k", "n", "c",
"a", "a", "n", "a", "j", "e", "k", "e",
"i", "a", "c", "c", "a", "e", "c", "8"
];
// Game state variables
let grid = [];
let revealed = [];
let flagged = [];
let gameOver = false;
let firstClick = true;
let width = 10;
let height = 10;
let mines = 15;
// DOM elements
const boardElement = document.getElementById('board');
const statusElement = document.getElementById('status');
const widthInput = document.getElementById('width');
const heightInput = document.getElementById('height');
const minesInput = document.getElementById('mines');
const newGameButton = document.getElementById('new-game');
// Initialize the game
function initGame() {
width = parseInt(widthInput.value) || 10;
height = parseInt(heightInput.value) || 10;
mines = parseInt(minesInput.value) || 15;
// Validate input
if (width < 5) width = 5;
if (width > 30) width = 30;
if (height < 5) height = 5;
if (height > 30) height = 30;
const maxMines = Math.floor(width * height * 0.3);
if (mines < 1) mines = 1;
if (mines > maxMines) mines = maxMines;
// Update input values
widthInput.value = width;
heightInput.value = height;
minesInput.value = mines;
// Reset game state
grid = Array(height).fill().map(() => Array(width).fill(0));
revealed = Array(height).fill().map(() => Array(width).fill(false));
flagged = Array(height).fill().map(() => Array(width).fill(false));
gameOver = false;
firstClick = true;
// Update status
statusElement.textContent = "Click to start";
// Render the board
renderBoard();
}
// Place mines after first click to ensure first cell is safe
function placeMines(firstRow, firstCol) {
let minesPlaced = 0;
while (minesPlaced < mines) {
const row = Math.floor(Math.random() * height);
const col = Math.floor(Math.random() * width);
// Skip if it's the first clicked cell or adjacent cells
if (Math.abs(row - firstRow) <= 1 && Math.abs(col - firstCol) <= 1) {
continue;
}
// Skip if there's already a mine
if (grid[row][col] === -1) {
continue;
}
// Place mine
grid[row][col] = -1;
minesPlaced++;
}
// Calculate numbers for non-mine cells
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
if (grid[row][col] !== -1) {
grid[row][col] = calculateRuleIndex(row, col);
}
}
}
}
// Calculate the rule index for a cell
function calculateRuleIndex(row, col) {
let index = 0;
let weight = 1;
// Check all 8 neighbors in the specified order
const neighbors = [
[row-1, col-1], [row-1, col], [row-1, col+1],
[row, col+1], [row+1, col+1], [row+1, col],
[row+1, col-1], [row, col-1]
];
for (let i = 0; i < neighbors.length; i++) {
const [r, c] = neighbors[i];
// Check if the neighbor is within bounds and is a mine
if (r >= 0 && r < height && c >= 0 && c < width && grid[r][c] === -1) {
index += weight;
}
weight *= 2;
}
return index;
}
// Reveal a cell
function revealCell(row, col) {
if (gameOver || revealed[row][col] || flagged[row][col]) {
return;
}
// First click - place mines and ensure safety
if (firstClick) {
firstClick = false;
placeMines(row, col);
// Recalculate the rule index for the first cell after mines are placed
grid[row][col] = calculateRuleIndex(row, col);
}
revealed[row][col] = true;
// Check if it's a mine
if (grid[row][col] === -1) {
gameOver = true;
statusElement.textContent = "Game Over!";
revealAllMines();
return;
}
// If it's a zero, reveal adjacent cells
if (grid[row][col] === 0) {
revealAdjacentCells(row, col);
}
// Check for win
if (checkWin()) {
gameOver = true;
statusElement.textContent = "You Win!";
return;
}
renderBoard();
}
// Reveal adjacent cells for zeros
function revealAdjacentCells(row, col) {
const directions = [
[-1, -1], [-1, 0], [-1, 1],
[0, -1], [0, 1],
[1, -1], [1, 0], [1, 1]
];
for (const [dr, dc] of directions) {
const newRow = row + dr;
const newCol = col + dc;
if (newRow >= 0 && newRow < height && newCol >= 0 && newCol < width) {
if (!revealed[newRow][newCol] && !flagged[newRow][newCol]) {
revealed[newRow][newCol] = true;
if (grid[newRow][newCol] === 0) {
revealAdjacentCells(newRow, newCol);
}
}
}
}
}
// Toggle flag on a cell
function toggleFlag(row, col) {
if (gameOver || revealed[row][col]) {
return;
}
flagged[row][col] = !flagged[row][col];
renderBoard();
}
// Check if the player has won
function checkWin() {
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
// If there's a non-mine cell that hasn't been revealed, game is not won
if (grid[row][col] !== -1 && !revealed[row][col]) {
return false;
}
}
}
return true;
}
// Reveal all mines when game is lost
function revealAllMines() {
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
if (grid[row][col] === -1) {
revealed[row][col] = true;
}
}
}
renderBoard();
}
// Render the game board
function renderBoard() {
// Clear the board
boardElement.innerHTML = '';
// Set grid template
boardElement.style.gridTemplateColumns = `repeat(${width}, 30px)`;
boardElement.style.gridTemplateRows = `repeat(${height}, 30px)`;
// Create cells
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
const cell = document.createElement('div');
cell.className = 'cell';
cell.dataset.row = row;
cell.dataset.col = col;
if (revealed[row][col]) {
cell.classList.add('revealed');
if (grid[row][col] === -1) {
cell.classList.add('mine');
cell.textContent = "💣";
} else {
const ruleIndex = grid[row][col];
cell.textContent = rulekeys[ruleIndex] || '';
}
} else if (flagged[row][col]) {
cell.classList.add('flagged');
}
// Add click events
cell.addEventListener('click', () => {
revealCell(row, col);
});
cell.addEventListener('contextmenu', (e) => {
e.preventDefault();
toggleFlag(row, col);
});
boardElement.appendChild(cell);
}
}
}
// Event listeners
newGameButton.addEventListener('click', initGame);
// Initialize the game
initGame();
</script>
</body>
</html>Edit 1:
The game can be played online at https://nd-zyth.github.io/int-minesweeper!