Our Process

Making a game in JS was definitly a challenge, but we were able to utilize inspiration from The Game of Life code and help from ChatGPT in order to make a game that is not only functional but looks aesthetically nice. Using containers in html was also a much simpler way to organize not only our JS but our CSS styling too.

%%html

<html>


<style>
    .container {
    text-align: center;
}

#board {
    display: grid;
    grid-template-columns: repeat(10, 30px);
    gap: 2px;
    margin: 20px auto;
}

.cell {
    width: 30px;
    height: 30px;
    background-color: #ccc;
    display: flex;
    justify-content: center;
    align-items: center;
    cursor: pointer;
}

#restart {
    margin-top: 20px;
    padding: 10px 20px;
    background-color: #007bff;
    color: #fff;
    border: none;
    border-radius: 5px;
    cursor: pointer;
}
</style>



<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Minesweeper</title>
</head>
<body>
    <div class="container">
        <h1>Minesweeper</h1>
        <div id="board"></div>
        <button id="restart">Restart</button>
    </div>
</body>

<script>
    document.addEventListener("DOMContentLoaded", function () {
    const board = document.getElementById("board");
    const restartButton = document.getElementById("restart");
    const size = 10; // Adjust the size of the board as needed
    const bombCount = 15; // Adjust the number of bombs as needed
    let cells = [];
    let bombs = [];

    // Initialize the board
    function initBoard() {
        cells = [];
        bombs = [];
        board.innerHTML = "";

        // Create cells
        for (let row = 0; row < size; row++) {
            for (let col = 0; col < size; col++) {
                const cell = document.createElement("div");
                cell.classList.add("cell");
                cell.dataset.row = row;
                cell.dataset.col = col;
                cell.addEventListener("click", handleCellClick); // Add click event listener here
                cells.push(cell);
                board.appendChild(cell);
            }
        }

        // Place bombs randomly
        while (bombs.length < bombCount) {
            const randomIndex = Math.floor(Math.random() * cells.length);
            const cell = cells[randomIndex];
            if (!cell.classList.contains("bomb")) {
                cell.classList.add("bomb");
                bombs.push(cell);
            }
        }
    }

    // Handle cell click
    function handleCellClick(event) {
        const cell = event.target;
        if (cell.classList.contains("opened") || cell.classList.contains("flagged")) {
            return;
        }

        if (cell.classList.contains("bomb")) {
            // Game over
            cell.classList.add("exploded");
            revealBombs();
            gameOver();
        } else {
            const row = parseInt(cell.dataset.row);
            const col = parseInt(cell.dataset.col);
            const bombCount = countAdjacentBombs(row, col);
            cell.classList.add("opened");
            if (bombCount > 0) {
                cell.textContent = bombCount;
            } else {
                // Auto-expand if no adjacent bombs
                expandEmptyArea(row, col);
            }
            checkWin();
        }
    }

    // Count adjacent bombs
    function countAdjacentBombs(row, col) {
        let count = 0;
        for (let i = -1; i <= 1; i++) {
            for (let j = -1; j <= 1; j++) {
                const r = row + i;
                const c = col + j;
                if (r >= 0 && r < size && c >= 0 && c < size) {
                    const adjacentCell = cells.find(
                        (cell) => cell.dataset.row == r && cell.dataset.col == c
                    );
                    if (adjacentCell.classList.contains("bomb")) {
                        count++;
                    }
                }
            }
        }
        return count;
    }

    // Expand empty area
    function expandEmptyArea(row, col) {
        const queue = [{ row, col }];
        const visited = new Set();

        while (queue.length > 0) {
            const { row, col } = queue.shift();
            const cell = cells.find(
                (c) => c.dataset.row == row && c.dataset.col == col
            );
            if (!cell || visited.has(`${row}-${col}`)) {
                continue;
            }
            visited.add(`${row}-${col}`);

            const bombCount = countAdjacentBombs(row, col);
            if (bombCount === 0) {
                cell.classList.add("opened");
                for (let i = -1; i <= 1; i++) {
                    for (let j = -1; j <= 1; j++) {
                        const r = row + i;
                        const c = col + j;
                        if (r >= 0 && r < size && c >= 0 && c < size) {
                            queue.push({ row: r, col: c });
                        }
                    }
                }
            } else {
                cell.classList.add("opened");
                cell.textContent = bombCount;
            }
        }
    }

    // Reveal all bombs
    function revealBombs() {
        bombs.forEach((bomb) => {
            bomb.classList.add("revealed-bomb");
            bomb.textContent = "X";
        });
    }

    // Game over
    function gameOver() {
        cells.forEach((cell) => {
            cell.removeEventListener("click", handleCellClick);
            if (cell.classList.contains("bomb")) {
                cell.classList.add("revealed-bomb");
            }
        });
        restartButton.style.display = "block";
    }

    // Check for a win
    function checkWin() {
        const unopenedCells = cells.filter(
            (cell) => !cell.classList.contains("opened")
        );
        const unopenedBombCells = unopenedCells.filter((cell) =>
            cell.classList.contains("bomb")
        );

        if (unopenedBombCells.length === 0) {
            // All non-bomb cells opened; player wins
            unopenedCells.forEach((cell) => {
                cell.classList.add("flagged");
            });
            restartButton.style.display = "block";
        }
    }

    // Restart the game
    restartButton.addEventListener("click", initBoard);

    // Initialize the game
    initBoard();
});

</script>

</html>

ChatGPT Review

The provided JavaScript Minesweeper code appears to be a functional implementation of the classic Minesweeper game. Overall, the code structure and logic seem well-organized and appropriately commented. Below is a review of the code with some feedback:

  1. Readability and Structure:
    • The code is well-structured and easy to read, with clear separation of concerns.
    • The use of comments to explain the purpose of functions and sections of code is commendable and enhances code readability.
  2. HTML and CSS:
    • The HTML and CSS sections are minimal but sufficient for this simple game.
    • The styling (CSS) is straightforward, making the game board and restart button visually appealing.
  3. Game Initialization:
    • The initBoard function initializes the game board by creating cells and randomly placing bombs. This function is clear and handles the core setup of the game.
  4. Event Handling:
    • Event listeners are appropriately added to cells and the restart button.
    • The click event on cells is used to reveal the content and handle various game states.
    • The restart button resets the game board when clicked, which is a nice feature.
  5. Bomb Placement:
    • Bombs are placed randomly, ensuring they are distributed across the board.
    • This randomness adds to the replayability of the game.
  6. Game Logic:
    • The game logic, including checking for adjacent bombs, expanding empty areas, and handling wins and losses, is well-implemented and follows the Minesweeper rules accurately.
  7. User Interface Feedback:
    • The code provides visual feedback to the user when a bomb is clicked (exploded bomb) or when the game is won (flagged cells).
    • Revealing the location of bombs upon game over is a standard Minesweeper feature.
  8. Improvement Suggestions:
    • Consider adding a timer to track the time taken to complete the game.
    • You could add difficulty levels or allow the user to customize the board size and bomb count.
    • Implementing right-click functionality for flagging cells would enhance the user experience.
    • You may want to improve the user interface by providing more information, such as the number of remaining flags or a message when the game is won or lost.
    • It might be beneficial to add some form of win/lose notification for accessibility purposes (e.g., screen reader support).

In summary, the code provided is a good starting point for a Minesweeper game. It demonstrates a solid understanding of the game’s mechanics and offers a clean, readable implementation. You can consider expanding on this codebase to add more features and enhance the user experience further.