Building a Minesweeper Game in JavaScript

Introduction to Minesweeper

Minesweeper is a classic puzzle game that has captured the hearts of many gamers and casual players alike. The objective of the game is to clear a grid without detonating hidden mines, using numerical clues to guide players on where it’s safe to click. In this article, we’ll dive into the process of developing a fully functional Minesweeper game using JavaScript, complete with HTML and CSS for styling.

Not only will we cover the core mechanics of the game, but we’ll also look into how to structure your code effectively, ensuring that our Minesweeper implementation is clean, modular, and easy to expand upon. Whether you’re a beginner looking to enhance your JavaScript skills or an experienced developer eager to create a game, this step-by-step guide will be beneficial.

By the end of this tutorial, you should feel confident in your ability to build a Minesweeper clone, and you may find inspiration to add your unique features or variations. Let’s get started!

Setting Up the Project

The first step in any project is setting up our development environment. We will use a simple HTML structure, styled with CSS, and powered by JavaScript. For this game, we need an HTML file, a CSS file for our styles, and a JavaScript file to handle the logic.

Create a new folder for your Minesweeper project and, inside it, create three files named index.html, style.css, and script.js. You can use any code editor you prefer. I recommend using Visual Studio Code or WebStorm for their powerful features and extensions.

Here’s a basic structure for our index.html file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Minesweeper</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1>Minesweeper Game</h1>
    <div id="game-board"></div>
    <script src="script.js"></script>
</body>
</html>

This code initializes a simple HTML document that includes a title and a game board where our Minesweeper grid will be rendered. We also link our CSS and JavaScript files.

Creating the Game Board

To build the Minesweeper board, we need to create a grid of clickable cells that will display whether they are empty, contain a number, or have a mine. Let’s use a two-dimensional array to represent our grid and populate it with mines.

In our script.js file, we’ll start by defining some global variables for our grid. We will specify the dimensions of the board and the number of mines:

const rows = 10;
const cols = 10;
const totalMines = 15;
let board = [];

Next, we’ll create a function to initialize our board. This function will create the grid, randomly place mines, and calculate the numbers that indicate how many mines are adjacent to each cell:

function initializeBoard() {
    // Create a 2D array
    for (let r = 0; r < rows; r++) {
        board[r] = [];
        for (let c = 0; c < cols; c++) {
            board[r][c] = { mine: false, adjacentMines: 0, revealed: false };
        }
    }

    // Place mines randomly
    let minesPlaced = 0;
    while (minesPlaced < totalMines) {
        const r = Math.floor(Math.random() * rows);
        const c = Math.floor(Math.random() * cols);
        if (!board[r][c].mine) {
            board[r][c].mine = true;
            minesPlaced++;
            updateAdjacentCells(r, c);
        }
    }
}

The updateAdjacentCells function is responsible for updating the adjacent mine counts for cells surrounding each mine:

function updateAdjacentCells(mineRow, mineCol) {
    for (let r = mineRow - 1; r <= mineRow + 1; r++) {
        for (let c = mineCol - 1; c <= mineCol + 1; c++) {
            if (r >= 0 && r < rows && c >= 0 && c < cols && !(r === mineRow && c === mineCol)) {
                board[r][c].adjacentMines++;
            }
        }
    }
}

This code manages the mines and updates the adjacent cells effectively. Next, we’ll need to render the board visually on our webpage.

Rendering the Game Board

Now that our Minesweeper board is set up, we must visually represent it on the screen. We’ll create a function called renderBoard that generates the HTML for the game board based on the state of the board array.

Here’s how we can implement the renderBoard function:

function renderBoard() {
    const gameBoard = document.getElementById('game-board');
    gameBoard.innerHTML = '';

    for (let r = 0; r < rows; r++) {
        const rowDiv = document.createElement('div');
        rowDiv.classList.add('row');

        for (let c = 0; c < cols; c++) {
            const cellDiv = document.createElement('div');
            cellDiv.classList.add('cell');
            cellDiv.dataset.row = r;
            cellDiv.dataset.col = c;
            cellDiv.onclick = handleCellClick;

            if (board[r][c].revealed) {
                if (board[r][c].mine) {
                    cellDiv.classList.add('mine');
                    cellDiv.innerHTML = '💣';
                } else if (board[r][c].adjacentMines > 0) {
                    cellDiv.innerHTML = board[r][c].adjacentMines;
                }
            }

            rowDiv.appendChild(cellDiv);
        }
        gameBoard.appendChild(rowDiv);
    }
}

In the above function, we loop through each row and column of our board, creating corresponding HTML elements for each cell. We attach an event listener that will handle cell clicks. Each cell will reveal whether it’s a mine or show the adjacent mine count if it’s already been revealed.

Handling Cell Clicks

Next, we need to implement the logic for clicking on cells. The handleCellClick function determines the cell clicked by the user and processes the game logic based on whether it’s a mine or not:

function handleCellClick(event) {
    const row = event.target.dataset.row;
    const col = event.target.dataset.col;

    if (board[row][col].revealed) return;
    board[row][col].revealed = true;

    if (board[row][col].mine) {
        alert('Game Over! You clicked on a mine.');
        revealAllMines();
    } else {
        // If the cell has no adjacent mines, reveal surrounding cells
        if (board[row][col].adjacentMines === 0) {
            revealSurroundingCells(row, col);
        }
    }

    renderBoard();
}

In the handleCellClick function, we first check if the cell has already been revealed to avoid double-clicking. If the clicked cell is a mine, we display a game over alert and reveal all mines. If not, we reveal the clicked cell and, if it has no adjacent mines, we recursively reveal surrounding cells using the revealSurroundingCells function.

Revealing Surrounding Cells

The revealSurroundingCells function is essential for Minesweeper as it allows players to clear large areas of the grid with one click, especially when clicking on cells with no adjacent mines:

function revealSurroundingCells(row, col) {
    for (let r = parseInt(row) - 1; r <= parseInt(row) + 1; r++) {
        for (let c = parseInt(col) - 1; c <= parseInt(col) + 1; c++) {
            if (r >= 0 && r < rows && c >= 0 && c < cols && !(r === parseInt(row) && c === parseInt(col))) {
                if (!board[r][c].revealed) {
                    board[r][c].revealed = true;
                    if (board[r][c].adjacentMines === 0) {
                        revealSurroundingCells(r, c);
                    }
                }
            }
        }
    }
}

This recursive function checks adjacent cells and reveals them. If an adjacent cell has no mines, it will continue to check its neighbors. This allows players to uncover larger sections of the grid quickly.

Styling the Game

While we’ve mostly focused on the functionality of our Minesweeper game, we can’t forget about styling. A well-designed interface improves user experience significantly. In our style.css file, let’s add some basic styles:

body {
    font-family: Arial, sans-serif;
    text-align: center;
}

#game-board {
    display: inline-block;
    margin: 20px auto;
}

.row {
    display: flex;
}

.cell {
    width: 30px;
    height: 30px;
    border: 1px solid #999;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}

.cell.revealed {
    background-color: #eee;
}

.mine {
    background-color: red;
    color: white;
}

These styles provide a basic visual structure for our game board, including cell dimensions, borders, and colors for different states like revealed cells and mines. You can customize these styles to fit your desired aesthetic, perhaps adding hover effects or transitions for a touch of elegance.

Enhancing the Game

Now that we have a working Minesweeper game, you might be asking yourself how to enhance it further. Here are a few suggestions for improvements:

  • Add a timer: Keep track of how much time has passed since the game began and display it to the player.
  • Implement a flagging system: Allow players to right-click on cells to flag potential mines, providing a visual indication that they suspect a mine is buried there.
  • Create a scoring system: Develop a way to track the player’s score based on how quickly they clear the board and how many flags they use.
  • Responsive design: Ensure that the game looks great on all device sizes, making it accessible for mobile users.

The beauty of developing games is that they can be continually improved upon, giving you the opportunity to learn new techniques and technologies. As you explore these enhancements, you can experiment with more advanced JavaScript features or dive into modern frameworks like React to create a more dynamic interface.

Conclusion

In this tutorial, we’ve built a simple yet functional Minesweeper game using plain JavaScript, HTML, and CSS. By learning to manage a grid of data, implement user interactions, and reveal game states, you’ve gained insight into essential JavaScript concepts and patterns that can be applied to other projects.

This exercise not only bolstered your JavaScript skills but also showcased the importance of a well-structured codebase. As you move forward, remember that the best way to master JavaScript is through hands-on projects—so feel free to take this Minesweeper game as a springboard for your next development adventure.

Don’t forget to share your Minesweeper game with the developer community! Seek feedback, make improvements, and inspire others to explore the wonderful world of JavaScript game development.

Scroll to Top