Skip to main content
Code Review

Return to Question

added 1 character in body
Source Link
  • Is the use of HTML semantics correct? I would like to know if I am using HTML tags properly and if there is any way to improve the semantic structure of the code.
  • Is there any way to improve theorganizationthe organization and reusability of the CSS code, any tips to make it more modular and easy to maintain?
  • Is there any way to optimize JavaScript code to make it more efficient and elegant? Any advice on best practices in terms of architecture or code writing?
  • Is the use of HTML semantics correct? I would like to know if I am using HTML tags properly and if there is any way to improve the semantic structure of the code.
  • Is there any way to improve theorganization and reusability of the CSS code, any tips to make it more modular and easy to maintain?
  • Is there any way to optimize JavaScript code to make it more efficient and elegant? Any advice on best practices in terms of architecture or code writing?
  • Is the use of HTML semantics correct? I would like to know if I am using HTML tags properly and if there is any way to improve the semantic structure of the code.
  • Is there any way to improve the organization and reusability of the CSS code, any tips to make it more modular and easy to maintain?
  • Is there any way to optimize JavaScript code to make it more efficient and elegant? Any advice on best practices in terms of architecture or code writing?
added 57 characters in body
Source Link

Code:

const BOARD = document.getElementById("board");
const REMAINING_FLAGS_ELEMENT = document.getElementById("remaining-flags");
const NEW_GAME_BUTTON = document.getElementById("new-game");
const LEVEL_BUTTONS = {
 beginner: document.getElementById("beginner"),
 intermediate: document.getElementById("intermediate"),
 advanced: document.getElementById("advanced"),
};
const LEVEL_SETTINGS = {
 beginner: { rows: 9, cols: 9, mines: 10 },
 intermediate: { rows: 16, cols: 16, mines: 40 },
 advanced: { rows: 16, cols: 30, mines: 99 },
};
let currentLevel = "beginner";
let currentLevelConfig = LEVEL_SETTINGS[currentLevel];
let rows = currentLevelConfig.rows;
let columns = currentLevelConfig.cols;
let remainingMines = LEVEL_SETTINGS[currentLevel].mines;
let remainingFlags = remainingMines;
let totalCellsRevealed = 0;
let correctFlagsCount = 0;
let boardArray = [];
let gameFinish;
/**
 * Creates the game board by generating the HTML table structure.
 * Initializes the game board array.
 * Updates the remaining flags count displayed on the webpage.
 * Places the mines randomly on the board.
 * Counts the number of adjacent mines for each cell.
 */
function createBoard() {
 const BOARD_FRAGMENT = document.createDocumentFragment();
 BOARD.textContent = "";
 for (let i = 0; i < rows; i++) {
 const ROW = document.createElement("tr");
 boardArray[i] = [];
 for (let j = 0; j < columns; j++) {
 const CELL = document.createElement("td");
 boardArray[i][j] = 0;
 ROW.appendChild(CELL);
 }
 BOARD_FRAGMENT.appendChild(ROW);
 }
 BOARD.appendChild(BOARD_FRAGMENT);
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 placeMines();
 countAdjacentMines();
}
/**
 * Randomly places the mines on the game board.
 * Updates the "boardArray" with the "mine" value for each mine location.
 */
function placeMines() {
 let minesToPlace = remainingMines;
 while (minesToPlace > 0) {
 const RANDOM_ROW = Math.floor(Math.random() * rows);
 const RANDOM_COL = Math.floor(Math.random() * columns);
 if (boardArray[RANDOM_ROW][RANDOM_COL] !== "mine") {
 boardArray[RANDOM_ROW][RANDOM_COL] = "mine";
 minesToPlace--;
 }
 }
}
/**
 * Counts the number of adjacent mines for each non-mine cell on the game board.
 * Updates the "boardArray" with the corresponding mine count for each cell.
 */
function countAdjacentMines() {
 for (let row = 0; row < rows; row++) {
 for (let col = 0; col < columns; col++) {
 if (boardArray[row][col] !== "mine") {
 let minesCount = 0;
 for (let i = row - 1; i <= row + 1; i++) {
 for (let j = col - 1; j <= col + 1; j++) {
 const VALID_ROW = i >= 0 && i < rows;
 const VALID_COL = j >= 0 && j < columns;
 if (VALID_ROW && VALID_COL && boardArray[i][j] === "mine") {
 minesCount++;
 }
 }
 }
 boardArray[row][col] = minesCount;
 }
 }
 }
}
/**
 * Reveals the content of a cell and handles game logic.
 *
 * @param {number} row - The row index of the cell.
 * @param {number} col - The column index of the cell.
 */
function revealCell(row, col) {
 const CELL = BOARD.rows[row].cells[col];
 if (CELL.classList.contains("flag") || CELL.textContent || gameFinish) return;
 if (boardArray[row][col] === "mine") {
 gameFinish = true;
 revealMines();
 alert("Game over! You hit a mine.");
 } else if (boardArray[row][col] === 0) {
 revealAdjacentsCells(row, col);
 } else {
 const NUMBER_CLASS = getNumberClass(boardArray[row][col]);
 CELL.textContent = boardArray[row][col];
 CELL.classList.add(NUMBER_CLASS);
 }
 totalCellsRevealed++;
 if (checkWin()) {
 gameFinish = true;
 alert("You win!");
 return;
 }
}
/**
 * Reveals adjacents cells surrounding the specified cell.
 *
 * @param {number} row - The row index of the cell.
 * @param {number} col - The column index of the cell.
 */
function revealAdjacentsCells(row, col) {
 const CELL = BOARD.rows[row].cells[col];
 if (CELL.textContent) return;
 CELL.classList.add("zero");
 for (let i = row - 1; i <= row + 1; i++) {
 for (let j = col - 1; j <= col + 1; j++) {
 const VALID_ROW = i >= 0 && i < rows;
 const VALID_COL = j >= 0 && j < columns;
 if (VALID_ROW && VALID_COL && !(i === row && j === col)) {
 const CELL = BOARD.rows[i].cells[j];
 if (!CELL.classList.value) revealCell(i, j);
 }
 }
 }
}
/**
 * Reveals all the mines on the game board.
 * Adds the "mine" class to the HTML elements representing mine cells.
 */
function revealMines() {
 for (let i = 0; i < rows; i++) {
 for (let j = 0; j < columns; j++) {
 if (boardArray[i][j] === "mine") {
 const MINE_CELL = BOARD.rows[i].cells[j];
 MINE_CELL.classList.add("mine");
 }
 }
 }
}
/**
 * Returns the CSS class name for a given number.
 *
 * @param {number} number - The number of adjacent mines.
 * @returns {string} The CSS class name for the number.
 */
function getNumberClass(number) {
 switch (number) {
 case 1:
 return "one";
 case 2:
 return "two";
 case 3:
 return "three";
 case 4:
 return "four";
 case 5:
 return "five";
 case 6:
 return "six";
 case 7:
 return "seven";
 case 8:
 return "eight";
 default:
 return "";
 }
}
/**
 * Changes the game level to the specified level.
 *
 * @param {string} level - The level to change to.
 */
function changeLevel(level) {
 if (currentLevel === level) return;
 gameFinish = false;
 LEVEL_BUTTONS[currentLevel].classList.remove("active");
 currentLevel = level;
 LEVEL_BUTTONS[currentLevel].classList.add("active");
 currentLevelConfig = LEVEL_SETTINGS[currentLevel];
 rows = currentLevelConfig.rows;
 columns = currentLevelConfig.cols;
 remainingMines = currentLevelConfig.mines;
 remainingFlags = remainingMines;
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 createBoard();
}
/**
 * Toggles the flag on a cell when the player right-clicks on it.
 *
 * @param {HTMLElement} cell - The HTML element representing the cell.
 */
function addFlagToCell(cell) {
 if (cell.classList.contains("zero") || cell.textContent || gameFinish) return;
 const HAS_FLAG = cell.classList.contains("flag");
 const ROW = cell.parentNode.rowIndex;
 const COL = cell.cellIndex;
 cell.classList.toggle("flag", !HAS_FLAG);
 remainingFlags += HAS_FLAG ? 1 : -1;
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 if (!HAS_FLAG && boardArray[ROW][COL] === "mine") correctFlagsCount++;
 if (checkWin()) {
 gameFinish = true;
 alert("You win!");
 return;
 }
}
/**
 * Checks if the player has won the game.
 * Returns true if all non-mine cells have been revealed and all flags are correctly placed on mine cells.
 *
 * @returns {boolean} True if the player has won, false otherwise.
 */
function checkWin() {
 return (
 totalCellsRevealed === rows * columns - remainingMines &&
 correctFlagsCount === remainingMines
 );
}
/**
 * Resets the game by resetting the game variables and creating a new board.
 */
function newGame() {
 gameFinish = false;
 correctFlagsCount = 0;
 totalCellsRevealed = 0;
 remainingMines = currentLevelConfig.mines;
 remainingFlags = remainingMines;
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 createBoard();
}
document.addEventListener("click", (event) => {
 const TARGET = event.target;
 if (TARGET.tagName === "TD") {
 const ROW = TARGET.parentNode.rowIndex;
 const COL = TARGET.cellIndex;
 revealCell(ROW, COL);
 } else if (TARGET === LEVEL_BUTTONS["beginner"]) {
 changeLevel("beginner");
 } else if (TARGET === LEVEL_BUTTONS["intermediate"]) {
 changeLevel("intermediate");
 } else if (TARGET === LEVEL_BUTTONS["advanced"]) {
 changeLevel("advanced");
 } else if (TARGET === NEW_GAME_BUTTON) {
 newGame();
 }
});
document.addEventListener("contextmenu", (event) => {
 const TARGET = event.target;
 if (TARGET.tagName === "TD") {
 event.preventDefault();
 addFlagToCell(TARGET);
 }
});
createBoard();
body {
 background-color: #55ddff;
 font-family: Arial, sans-serif;
}
h1 {
 text-align: center;
 color: #263be8;
 margin-bottom: 0;
}
.minesweeper {
 display: flex;
 flex-direction: column;
 justify-content: center;
 align-items: center;
 gap: 10px;
}
.btn {
 background-color: #0000ff;
 border: 0;
 color: #fff;
 cursor: pointer;
 font-weight: bold;
 line-height: normal;
 border-radius: 5px;
 padding: 5px;
 margin-left: 8px;
}
.active {
 background-color: red;
}
.info {
 color: red;
 font-weight: bold;
 font-size: 20px;
}
table {
 border-spacing: 0px;
}
td {
 padding: 0;
 width: 25px;
 height: 25px;
 background-color: #fff;
 border: 1px solid #a1a1a1;
 text-align: center;
 line-height: 20px;
 font-weight: bold;
 font-size: 18px;
}
.mine {
 background: #eeeeee url(../img/mine.png) no-repeat center;
 background-size: cover;
}
.flag {
 background: #eeeeee url(../img/flag.png) no-repeat center;
 background-size: cover;
}
.zero {
 background-color: #eeeeee;
}
.one {
 background-color: #eeeeee;
 color: #0332fe;
}
.two {
 background-color: #eeeeee;
 color: #019f02;
}
.three {
 background-color: #eeeeee;
 color: #ff2600;
}
.four {
 background-color: #eeeeee;
 color: #93208f;
}
.five {
 background-color: #eeeeee;
 color: #ff7f29;
}
.six {
 background-color: #eeeeee;
 color: #ff3fff;
}
.seven {
 background-color: #eeeeee;
 color: #53b8b4;
}
.eight {
 background-color: #eeeeee;
 color: #22ee0f;
}
 
 
<!DOCTYPE html>
<html lang="en">
 <head>
 <title>Minesweeper</title>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <link rel="stylesheet" href="assets/css/normalize.css">
 <link rel="stylesheet" href="assets/css/style.css">
 <link rel="icon" href="assets/img/favicon.png">
 <script src="assets/js/index.js" defer></script>
 </head>
 <body>
 <section class="minesweeper">
 <h1>Minesweeper</h1>
 <div class="level">
 <button class="btn active" id="beginner">Beginner</button>
 <button class="btn" id="intermediate">Intermediate</button>
 <button class="btn" id="advanced">Advanced</button>
 <button class="btn" id="new-game">New game</button>
 </div>
 <p class="info">🚩 <span id="remaining-flags">10</span></p>
 <table id="board"></table>
 </section>
 </body>
</html>
 

Code:

<!DOCTYPE html>
<html lang="en">
 <head>
 <title>Minesweeper</title>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <link rel="stylesheet" href="assets/css/normalize.css">
 <link rel="stylesheet" href="assets/css/style.css">
 <link rel="icon" href="assets/img/favicon.png">
 <script src="assets/js/index.js" defer></script>
 </head>
 <body>
 <section class="minesweeper">
 <h1>Minesweeper</h1>
 <div class="level">
 <button class="btn active" id="beginner">Beginner</button>
 <button class="btn" id="intermediate">Intermediate</button>
 <button class="btn" id="advanced">Advanced</button>
 <button class="btn" id="new-game">New game</button>
 </div>
 <p class="info">🚩 <span id="remaining-flags">10</span></p>
 <table id="board"></table>
 </section>
 </body>
</html>
body {
 background-color: #55ddff;
 font-family: Arial, sans-serif;
}
h1 {
 text-align: center;
 color: #263be8;
 margin-bottom: 0;
}
.minesweeper {
 display: flex;
 flex-direction: column;
 justify-content: center;
 align-items: center;
 gap: 10px;
}
.btn {
 background-color: #0000ff;
 border: 0;
 color: #fff;
 cursor: pointer;
 font-weight: bold;
 line-height: normal;
 border-radius: 5px;
 padding: 5px;
 margin-left: 8px;
}
.active {
 background-color: red;
}
.info {
 color: red;
 font-weight: bold;
 font-size: 20px;
}
table {
 border-spacing: 0px;
}
td {
 padding: 0;
 width: 25px;
 height: 25px;
 background-color: #fff;
 border: 1px solid #a1a1a1;
 text-align: center;
 line-height: 20px;
 font-weight: bold;
 font-size: 18px;
}
.mine {
 background: #eeeeee url(../img/mine.png) no-repeat center;
 background-size: cover;
}
.flag {
 background: #eeeeee url(../img/flag.png) no-repeat center;
 background-size: cover;
}
.zero {
 background-color: #eeeeee;
}
.one {
 background-color: #eeeeee;
 color: #0332fe;
}
.two {
 background-color: #eeeeee;
 color: #019f02;
}
.three {
 background-color: #eeeeee;
 color: #ff2600;
}
.four {
 background-color: #eeeeee;
 color: #93208f;
}
.five {
 background-color: #eeeeee;
 color: #ff7f29;
}
.six {
 background-color: #eeeeee;
 color: #ff3fff;
}
.seven {
 background-color: #eeeeee;
 color: #53b8b4;
}
.eight {
 background-color: #eeeeee;
 color: #22ee0f;
}
const BOARD = document.getElementById("board");
const REMAINING_FLAGS_ELEMENT = document.getElementById("remaining-flags");
const NEW_GAME_BUTTON = document.getElementById("new-game");
const LEVEL_BUTTONS = {
 beginner: document.getElementById("beginner"),
 intermediate: document.getElementById("intermediate"),
 advanced: document.getElementById("advanced"),
};
const LEVEL_SETTINGS = {
 beginner: { rows: 9, cols: 9, mines: 10 },
 intermediate: { rows: 16, cols: 16, mines: 40 },
 advanced: { rows: 16, cols: 30, mines: 99 },
};
let currentLevel = "beginner";
let currentLevelConfig = LEVEL_SETTINGS[currentLevel];
let rows = currentLevelConfig.rows;
let columns = currentLevelConfig.cols;
let remainingMines = LEVEL_SETTINGS[currentLevel].mines;
let remainingFlags = remainingMines;
let totalCellsRevealed = 0;
let correctFlagsCount = 0;
let boardArray = [];
let gameFinish;
/**
 * Creates the game board by generating the HTML table structure.
 * Initializes the game board array.
 * Updates the remaining flags count displayed on the webpage.
 * Places the mines randomly on the board.
 * Counts the number of adjacent mines for each cell.
 */
function createBoard() {
 const BOARD_FRAGMENT = document.createDocumentFragment();
 BOARD.textContent = "";
 for (let i = 0; i < rows; i++) {
 const ROW = document.createElement("tr");
 boardArray[i] = [];
 for (let j = 0; j < columns; j++) {
 const CELL = document.createElement("td");
 boardArray[i][j] = 0;
 ROW.appendChild(CELL);
 }
 BOARD_FRAGMENT.appendChild(ROW);
 }
 BOARD.appendChild(BOARD_FRAGMENT);
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 placeMines();
 countAdjacentMines();
}
/**
 * Randomly places the mines on the game board.
 * Updates the "boardArray" with the "mine" value for each mine location.
 */
function placeMines() {
 let minesToPlace = remainingMines;
 while (minesToPlace > 0) {
 const RANDOM_ROW = Math.floor(Math.random() * rows);
 const RANDOM_COL = Math.floor(Math.random() * columns);
 if (boardArray[RANDOM_ROW][RANDOM_COL] !== "mine") {
 boardArray[RANDOM_ROW][RANDOM_COL] = "mine";
 minesToPlace--;
 }
 }
}
/**
 * Counts the number of adjacent mines for each non-mine cell on the game board.
 * Updates the "boardArray" with the corresponding mine count for each cell.
 */
function countAdjacentMines() {
 for (let row = 0; row < rows; row++) {
 for (let col = 0; col < columns; col++) {
 if (boardArray[row][col] !== "mine") {
 let minesCount = 0;
 for (let i = row - 1; i <= row + 1; i++) {
 for (let j = col - 1; j <= col + 1; j++) {
 const VALID_ROW = i >= 0 && i < rows;
 const VALID_COL = j >= 0 && j < columns;
 if (VALID_ROW && VALID_COL && boardArray[i][j] === "mine") {
 minesCount++;
 }
 }
 }
 boardArray[row][col] = minesCount;
 }
 }
 }
}
/**
 * Reveals the content of a cell and handles game logic.
 *
 * @param {number} row - The row index of the cell.
 * @param {number} col - The column index of the cell.
 */
function revealCell(row, col) {
 const CELL = BOARD.rows[row].cells[col];
 if (CELL.classList.contains("flag") || CELL.textContent || gameFinish) return;
 if (boardArray[row][col] === "mine") {
 gameFinish = true;
 revealMines();
 alert("Game over! You hit a mine.");
 } else if (boardArray[row][col] === 0) {
 revealAdjacentsCells(row, col);
 } else {
 const NUMBER_CLASS = getNumberClass(boardArray[row][col]);
 CELL.textContent = boardArray[row][col];
 CELL.classList.add(NUMBER_CLASS);
 }
 totalCellsRevealed++;
 if (checkWin()) {
 gameFinish = true;
 alert("You win!");
 return;
 }
}
/**
 * Reveals adjacents cells surrounding the specified cell.
 *
 * @param {number} row - The row index of the cell.
 * @param {number} col - The column index of the cell.
 */
function revealAdjacentsCells(row, col) {
 const CELL = BOARD.rows[row].cells[col];
 if (CELL.textContent) return;
 CELL.classList.add("zero");
 for (let i = row - 1; i <= row + 1; i++) {
 for (let j = col - 1; j <= col + 1; j++) {
 const VALID_ROW = i >= 0 && i < rows;
 const VALID_COL = j >= 0 && j < columns;
 if (VALID_ROW && VALID_COL && !(i === row && j === col)) {
 const CELL = BOARD.rows[i].cells[j];
 if (!CELL.classList.value) revealCell(i, j);
 }
 }
 }
}
/**
 * Reveals all the mines on the game board.
 * Adds the "mine" class to the HTML elements representing mine cells.
 */
function revealMines() {
 for (let i = 0; i < rows; i++) {
 for (let j = 0; j < columns; j++) {
 if (boardArray[i][j] === "mine") {
 const MINE_CELL = BOARD.rows[i].cells[j];
 MINE_CELL.classList.add("mine");
 }
 }
 }
}
/**
 * Returns the CSS class name for a given number.
 *
 * @param {number} number - The number of adjacent mines.
 * @returns {string} The CSS class name for the number.
 */
function getNumberClass(number) {
 switch (number) {
 case 1:
 return "one";
 case 2:
 return "two";
 case 3:
 return "three";
 case 4:
 return "four";
 case 5:
 return "five";
 case 6:
 return "six";
 case 7:
 return "seven";
 case 8:
 return "eight";
 default:
 return "";
 }
}
/**
 * Changes the game level to the specified level.
 *
 * @param {string} level - The level to change to.
 */
function changeLevel(level) {
 if (currentLevel === level) return;
 gameFinish = false;
 LEVEL_BUTTONS[currentLevel].classList.remove("active");
 currentLevel = level;
 LEVEL_BUTTONS[currentLevel].classList.add("active");
 currentLevelConfig = LEVEL_SETTINGS[currentLevel];
 rows = currentLevelConfig.rows;
 columns = currentLevelConfig.cols;
 remainingMines = currentLevelConfig.mines;
 remainingFlags = remainingMines;
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 createBoard();
}
/**
 * Toggles the flag on a cell when the player right-clicks on it.
 *
 * @param {HTMLElement} cell - The HTML element representing the cell.
 */
function addFlagToCell(cell) {
 if (cell.classList.contains("zero") || cell.textContent || gameFinish) return;
 const HAS_FLAG = cell.classList.contains("flag");
 const ROW = cell.parentNode.rowIndex;
 const COL = cell.cellIndex;
 cell.classList.toggle("flag", !HAS_FLAG);
 remainingFlags += HAS_FLAG ? 1 : -1;
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 if (!HAS_FLAG && boardArray[ROW][COL] === "mine") correctFlagsCount++;
 if (checkWin()) {
 gameFinish = true;
 alert("You win!");
 return;
 }
}
/**
 * Checks if the player has won the game.
 * Returns true if all non-mine cells have been revealed and all flags are correctly placed on mine cells.
 *
 * @returns {boolean} True if the player has won, false otherwise.
 */
function checkWin() {
 return (
 totalCellsRevealed === rows * columns - remainingMines &&
 correctFlagsCount === remainingMines
 );
}
/**
 * Resets the game by resetting the game variables and creating a new board.
 */
function newGame() {
 gameFinish = false;
 correctFlagsCount = 0;
 totalCellsRevealed = 0;
 remainingMines = currentLevelConfig.mines;
 remainingFlags = remainingMines;
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 createBoard();
}
document.addEventListener("click", (event) => {
 const TARGET = event.target;
 if (TARGET.tagName === "TD") {
 const ROW = TARGET.parentNode.rowIndex;
 const COL = TARGET.cellIndex;
 revealCell(ROW, COL);
 } else if (TARGET === LEVEL_BUTTONS["beginner"]) {
 changeLevel("beginner");
 } else if (TARGET === LEVEL_BUTTONS["intermediate"]) {
 changeLevel("intermediate");
 } else if (TARGET === LEVEL_BUTTONS["advanced"]) {
 changeLevel("advanced");
 } else if (TARGET === NEW_GAME_BUTTON) {
 newGame();
 }
});
document.addEventListener("contextmenu", (event) => {
 const TARGET = event.target;
 if (TARGET.tagName === "TD") {
 event.preventDefault();
 addFlagToCell(TARGET);
 }
});
createBoard();

Github link
Live host

Code:

const BOARD = document.getElementById("board");
const REMAINING_FLAGS_ELEMENT = document.getElementById("remaining-flags");
const NEW_GAME_BUTTON = document.getElementById("new-game");
const LEVEL_BUTTONS = {
 beginner: document.getElementById("beginner"),
 intermediate: document.getElementById("intermediate"),
 advanced: document.getElementById("advanced"),
};
const LEVEL_SETTINGS = {
 beginner: { rows: 9, cols: 9, mines: 10 },
 intermediate: { rows: 16, cols: 16, mines: 40 },
 advanced: { rows: 16, cols: 30, mines: 99 },
};
let currentLevel = "beginner";
let currentLevelConfig = LEVEL_SETTINGS[currentLevel];
let rows = currentLevelConfig.rows;
let columns = currentLevelConfig.cols;
let remainingMines = LEVEL_SETTINGS[currentLevel].mines;
let remainingFlags = remainingMines;
let totalCellsRevealed = 0;
let correctFlagsCount = 0;
let boardArray = [];
let gameFinish;
/**
 * Creates the game board by generating the HTML table structure.
 * Initializes the game board array.
 * Updates the remaining flags count displayed on the webpage.
 * Places the mines randomly on the board.
 * Counts the number of adjacent mines for each cell.
 */
function createBoard() {
 const BOARD_FRAGMENT = document.createDocumentFragment();
 BOARD.textContent = "";
 for (let i = 0; i < rows; i++) {
 const ROW = document.createElement("tr");
 boardArray[i] = [];
 for (let j = 0; j < columns; j++) {
 const CELL = document.createElement("td");
 boardArray[i][j] = 0;
 ROW.appendChild(CELL);
 }
 BOARD_FRAGMENT.appendChild(ROW);
 }
 BOARD.appendChild(BOARD_FRAGMENT);
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 placeMines();
 countAdjacentMines();
}
/**
 * Randomly places the mines on the game board.
 * Updates the "boardArray" with the "mine" value for each mine location.
 */
function placeMines() {
 let minesToPlace = remainingMines;
 while (minesToPlace > 0) {
 const RANDOM_ROW = Math.floor(Math.random() * rows);
 const RANDOM_COL = Math.floor(Math.random() * columns);
 if (boardArray[RANDOM_ROW][RANDOM_COL] !== "mine") {
 boardArray[RANDOM_ROW][RANDOM_COL] = "mine";
 minesToPlace--;
 }
 }
}
/**
 * Counts the number of adjacent mines for each non-mine cell on the game board.
 * Updates the "boardArray" with the corresponding mine count for each cell.
 */
function countAdjacentMines() {
 for (let row = 0; row < rows; row++) {
 for (let col = 0; col < columns; col++) {
 if (boardArray[row][col] !== "mine") {
 let minesCount = 0;
 for (let i = row - 1; i <= row + 1; i++) {
 for (let j = col - 1; j <= col + 1; j++) {
 const VALID_ROW = i >= 0 && i < rows;
 const VALID_COL = j >= 0 && j < columns;
 if (VALID_ROW && VALID_COL && boardArray[i][j] === "mine") {
 minesCount++;
 }
 }
 }
 boardArray[row][col] = minesCount;
 }
 }
 }
}
/**
 * Reveals the content of a cell and handles game logic.
 *
 * @param {number} row - The row index of the cell.
 * @param {number} col - The column index of the cell.
 */
function revealCell(row, col) {
 const CELL = BOARD.rows[row].cells[col];
 if (CELL.classList.contains("flag") || CELL.textContent || gameFinish) return;
 if (boardArray[row][col] === "mine") {
 gameFinish = true;
 revealMines();
 alert("Game over! You hit a mine.");
 } else if (boardArray[row][col] === 0) {
 revealAdjacentsCells(row, col);
 } else {
 const NUMBER_CLASS = getNumberClass(boardArray[row][col]);
 CELL.textContent = boardArray[row][col];
 CELL.classList.add(NUMBER_CLASS);
 }
 totalCellsRevealed++;
 if (checkWin()) {
 gameFinish = true;
 alert("You win!");
 return;
 }
}
/**
 * Reveals adjacents cells surrounding the specified cell.
 *
 * @param {number} row - The row index of the cell.
 * @param {number} col - The column index of the cell.
 */
function revealAdjacentsCells(row, col) {
 const CELL = BOARD.rows[row].cells[col];
 if (CELL.textContent) return;
 CELL.classList.add("zero");
 for (let i = row - 1; i <= row + 1; i++) {
 for (let j = col - 1; j <= col + 1; j++) {
 const VALID_ROW = i >= 0 && i < rows;
 const VALID_COL = j >= 0 && j < columns;
 if (VALID_ROW && VALID_COL && !(i === row && j === col)) {
 const CELL = BOARD.rows[i].cells[j];
 if (!CELL.classList.value) revealCell(i, j);
 }
 }
 }
}
/**
 * Reveals all the mines on the game board.
 * Adds the "mine" class to the HTML elements representing mine cells.
 */
function revealMines() {
 for (let i = 0; i < rows; i++) {
 for (let j = 0; j < columns; j++) {
 if (boardArray[i][j] === "mine") {
 const MINE_CELL = BOARD.rows[i].cells[j];
 MINE_CELL.classList.add("mine");
 }
 }
 }
}
/**
 * Returns the CSS class name for a given number.
 *
 * @param {number} number - The number of adjacent mines.
 * @returns {string} The CSS class name for the number.
 */
function getNumberClass(number) {
 switch (number) {
 case 1:
 return "one";
 case 2:
 return "two";
 case 3:
 return "three";
 case 4:
 return "four";
 case 5:
 return "five";
 case 6:
 return "six";
 case 7:
 return "seven";
 case 8:
 return "eight";
 default:
 return "";
 }
}
/**
 * Changes the game level to the specified level.
 *
 * @param {string} level - The level to change to.
 */
function changeLevel(level) {
 if (currentLevel === level) return;
 gameFinish = false;
 LEVEL_BUTTONS[currentLevel].classList.remove("active");
 currentLevel = level;
 LEVEL_BUTTONS[currentLevel].classList.add("active");
 currentLevelConfig = LEVEL_SETTINGS[currentLevel];
 rows = currentLevelConfig.rows;
 columns = currentLevelConfig.cols;
 remainingMines = currentLevelConfig.mines;
 remainingFlags = remainingMines;
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 createBoard();
}
/**
 * Toggles the flag on a cell when the player right-clicks on it.
 *
 * @param {HTMLElement} cell - The HTML element representing the cell.
 */
function addFlagToCell(cell) {
 if (cell.classList.contains("zero") || cell.textContent || gameFinish) return;
 const HAS_FLAG = cell.classList.contains("flag");
 const ROW = cell.parentNode.rowIndex;
 const COL = cell.cellIndex;
 cell.classList.toggle("flag", !HAS_FLAG);
 remainingFlags += HAS_FLAG ? 1 : -1;
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 if (!HAS_FLAG && boardArray[ROW][COL] === "mine") correctFlagsCount++;
 if (checkWin()) {
 gameFinish = true;
 alert("You win!");
 return;
 }
}
/**
 * Checks if the player has won the game.
 * Returns true if all non-mine cells have been revealed and all flags are correctly placed on mine cells.
 *
 * @returns {boolean} True if the player has won, false otherwise.
 */
function checkWin() {
 return (
 totalCellsRevealed === rows * columns - remainingMines &&
 correctFlagsCount === remainingMines
 );
}
/**
 * Resets the game by resetting the game variables and creating a new board.
 */
function newGame() {
 gameFinish = false;
 correctFlagsCount = 0;
 totalCellsRevealed = 0;
 remainingMines = currentLevelConfig.mines;
 remainingFlags = remainingMines;
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 createBoard();
}
document.addEventListener("click", (event) => {
 const TARGET = event.target;
 if (TARGET.tagName === "TD") {
 const ROW = TARGET.parentNode.rowIndex;
 const COL = TARGET.cellIndex;
 revealCell(ROW, COL);
 } else if (TARGET === LEVEL_BUTTONS["beginner"]) {
 changeLevel("beginner");
 } else if (TARGET === LEVEL_BUTTONS["intermediate"]) {
 changeLevel("intermediate");
 } else if (TARGET === LEVEL_BUTTONS["advanced"]) {
 changeLevel("advanced");
 } else if (TARGET === NEW_GAME_BUTTON) {
 newGame();
 }
});
document.addEventListener("contextmenu", (event) => {
 const TARGET = event.target;
 if (TARGET.tagName === "TD") {
 event.preventDefault();
 addFlagToCell(TARGET);
 }
});
createBoard();
body {
 background-color: #55ddff;
 font-family: Arial, sans-serif;
}
h1 {
 text-align: center;
 color: #263be8;
 margin-bottom: 0;
}
.minesweeper {
 display: flex;
 flex-direction: column;
 justify-content: center;
 align-items: center;
 gap: 10px;
}
.btn {
 background-color: #0000ff;
 border: 0;
 color: #fff;
 cursor: pointer;
 font-weight: bold;
 line-height: normal;
 border-radius: 5px;
 padding: 5px;
 margin-left: 8px;
}
.active {
 background-color: red;
}
.info {
 color: red;
 font-weight: bold;
 font-size: 20px;
}
table {
 border-spacing: 0px;
}
td {
 padding: 0;
 width: 25px;
 height: 25px;
 background-color: #fff;
 border: 1px solid #a1a1a1;
 text-align: center;
 line-height: 20px;
 font-weight: bold;
 font-size: 18px;
}
.mine {
 background: #eeeeee url(../img/mine.png) no-repeat center;
 background-size: cover;
}
.flag {
 background: #eeeeee url(../img/flag.png) no-repeat center;
 background-size: cover;
}
.zero {
 background-color: #eeeeee;
}
.one {
 background-color: #eeeeee;
 color: #0332fe;
}
.two {
 background-color: #eeeeee;
 color: #019f02;
}
.three {
 background-color: #eeeeee;
 color: #ff2600;
}
.four {
 background-color: #eeeeee;
 color: #93208f;
}
.five {
 background-color: #eeeeee;
 color: #ff7f29;
}
.six {
 background-color: #eeeeee;
 color: #ff3fff;
}
.seven {
 background-color: #eeeeee;
 color: #53b8b4;
}
.eight {
 background-color: #eeeeee;
 color: #22ee0f;
}
 
 
<!DOCTYPE html>
<html lang="en">
 <head>
 <title>Minesweeper</title>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <link rel="stylesheet" href="assets/css/normalize.css">
 <link rel="stylesheet" href="assets/css/style.css">
 <link rel="icon" href="assets/img/favicon.png">
 <script src="assets/js/index.js" defer></script>
 </head>
 <body>
 <section class="minesweeper">
 <h1>Minesweeper</h1>
 <div class="level">
 <button class="btn active" id="beginner">Beginner</button>
 <button class="btn" id="intermediate">Intermediate</button>
 <button class="btn" id="advanced">Advanced</button>
 <button class="btn" id="new-game">New game</button>
 </div>
 <p class="info">🚩 <span id="remaining-flags">10</span></p>
 <table id="board"></table>
 </section>
 </body>
</html>
 

Github link

Code:

<!DOCTYPE html>
<html lang="en">
 <head>
 <title>Minesweeper</title>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <link rel="stylesheet" href="assets/css/normalize.css">
 <link rel="stylesheet" href="assets/css/style.css">
 <link rel="icon" href="assets/img/favicon.png">
 <script src="assets/js/index.js" defer></script>
 </head>
 <body>
 <section class="minesweeper">
 <h1>Minesweeper</h1>
 <div class="level">
 <button class="btn active" id="beginner">Beginner</button>
 <button class="btn" id="intermediate">Intermediate</button>
 <button class="btn" id="advanced">Advanced</button>
 <button class="btn" id="new-game">New game</button>
 </div>
 <p class="info">🚩 <span id="remaining-flags">10</span></p>
 <table id="board"></table>
 </section>
 </body>
</html>
body {
 background-color: #55ddff;
 font-family: Arial, sans-serif;
}
h1 {
 text-align: center;
 color: #263be8;
 margin-bottom: 0;
}
.minesweeper {
 display: flex;
 flex-direction: column;
 justify-content: center;
 align-items: center;
 gap: 10px;
}
.btn {
 background-color: #0000ff;
 border: 0;
 color: #fff;
 cursor: pointer;
 font-weight: bold;
 line-height: normal;
 border-radius: 5px;
 padding: 5px;
 margin-left: 8px;
}
.active {
 background-color: red;
}
.info {
 color: red;
 font-weight: bold;
 font-size: 20px;
}
table {
 border-spacing: 0px;
}
td {
 padding: 0;
 width: 25px;
 height: 25px;
 background-color: #fff;
 border: 1px solid #a1a1a1;
 text-align: center;
 line-height: 20px;
 font-weight: bold;
 font-size: 18px;
}
.mine {
 background: #eeeeee url(../img/mine.png) no-repeat center;
 background-size: cover;
}
.flag {
 background: #eeeeee url(../img/flag.png) no-repeat center;
 background-size: cover;
}
.zero {
 background-color: #eeeeee;
}
.one {
 background-color: #eeeeee;
 color: #0332fe;
}
.two {
 background-color: #eeeeee;
 color: #019f02;
}
.three {
 background-color: #eeeeee;
 color: #ff2600;
}
.four {
 background-color: #eeeeee;
 color: #93208f;
}
.five {
 background-color: #eeeeee;
 color: #ff7f29;
}
.six {
 background-color: #eeeeee;
 color: #ff3fff;
}
.seven {
 background-color: #eeeeee;
 color: #53b8b4;
}
.eight {
 background-color: #eeeeee;
 color: #22ee0f;
}
const BOARD = document.getElementById("board");
const REMAINING_FLAGS_ELEMENT = document.getElementById("remaining-flags");
const NEW_GAME_BUTTON = document.getElementById("new-game");
const LEVEL_BUTTONS = {
 beginner: document.getElementById("beginner"),
 intermediate: document.getElementById("intermediate"),
 advanced: document.getElementById("advanced"),
};
const LEVEL_SETTINGS = {
 beginner: { rows: 9, cols: 9, mines: 10 },
 intermediate: { rows: 16, cols: 16, mines: 40 },
 advanced: { rows: 16, cols: 30, mines: 99 },
};
let currentLevel = "beginner";
let currentLevelConfig = LEVEL_SETTINGS[currentLevel];
let rows = currentLevelConfig.rows;
let columns = currentLevelConfig.cols;
let remainingMines = LEVEL_SETTINGS[currentLevel].mines;
let remainingFlags = remainingMines;
let totalCellsRevealed = 0;
let correctFlagsCount = 0;
let boardArray = [];
let gameFinish;
/**
 * Creates the game board by generating the HTML table structure.
 * Initializes the game board array.
 * Updates the remaining flags count displayed on the webpage.
 * Places the mines randomly on the board.
 * Counts the number of adjacent mines for each cell.
 */
function createBoard() {
 const BOARD_FRAGMENT = document.createDocumentFragment();
 BOARD.textContent = "";
 for (let i = 0; i < rows; i++) {
 const ROW = document.createElement("tr");
 boardArray[i] = [];
 for (let j = 0; j < columns; j++) {
 const CELL = document.createElement("td");
 boardArray[i][j] = 0;
 ROW.appendChild(CELL);
 }
 BOARD_FRAGMENT.appendChild(ROW);
 }
 BOARD.appendChild(BOARD_FRAGMENT);
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 placeMines();
 countAdjacentMines();
}
/**
 * Randomly places the mines on the game board.
 * Updates the "boardArray" with the "mine" value for each mine location.
 */
function placeMines() {
 let minesToPlace = remainingMines;
 while (minesToPlace > 0) {
 const RANDOM_ROW = Math.floor(Math.random() * rows);
 const RANDOM_COL = Math.floor(Math.random() * columns);
 if (boardArray[RANDOM_ROW][RANDOM_COL] !== "mine") {
 boardArray[RANDOM_ROW][RANDOM_COL] = "mine";
 minesToPlace--;
 }
 }
}
/**
 * Counts the number of adjacent mines for each non-mine cell on the game board.
 * Updates the "boardArray" with the corresponding mine count for each cell.
 */
function countAdjacentMines() {
 for (let row = 0; row < rows; row++) {
 for (let col = 0; col < columns; col++) {
 if (boardArray[row][col] !== "mine") {
 let minesCount = 0;
 for (let i = row - 1; i <= row + 1; i++) {
 for (let j = col - 1; j <= col + 1; j++) {
 const VALID_ROW = i >= 0 && i < rows;
 const VALID_COL = j >= 0 && j < columns;
 if (VALID_ROW && VALID_COL && boardArray[i][j] === "mine") {
 minesCount++;
 }
 }
 }
 boardArray[row][col] = minesCount;
 }
 }
 }
}
/**
 * Reveals the content of a cell and handles game logic.
 *
 * @param {number} row - The row index of the cell.
 * @param {number} col - The column index of the cell.
 */
function revealCell(row, col) {
 const CELL = BOARD.rows[row].cells[col];
 if (CELL.classList.contains("flag") || CELL.textContent || gameFinish) return;
 if (boardArray[row][col] === "mine") {
 gameFinish = true;
 revealMines();
 alert("Game over! You hit a mine.");
 } else if (boardArray[row][col] === 0) {
 revealAdjacentsCells(row, col);
 } else {
 const NUMBER_CLASS = getNumberClass(boardArray[row][col]);
 CELL.textContent = boardArray[row][col];
 CELL.classList.add(NUMBER_CLASS);
 }
 totalCellsRevealed++;
 if (checkWin()) {
 gameFinish = true;
 alert("You win!");
 return;
 }
}
/**
 * Reveals adjacents cells surrounding the specified cell.
 *
 * @param {number} row - The row index of the cell.
 * @param {number} col - The column index of the cell.
 */
function revealAdjacentsCells(row, col) {
 const CELL = BOARD.rows[row].cells[col];
 if (CELL.textContent) return;
 CELL.classList.add("zero");
 for (let i = row - 1; i <= row + 1; i++) {
 for (let j = col - 1; j <= col + 1; j++) {
 const VALID_ROW = i >= 0 && i < rows;
 const VALID_COL = j >= 0 && j < columns;
 if (VALID_ROW && VALID_COL && !(i === row && j === col)) {
 const CELL = BOARD.rows[i].cells[j];
 if (!CELL.classList.value) revealCell(i, j);
 }
 }
 }
}
/**
 * Reveals all the mines on the game board.
 * Adds the "mine" class to the HTML elements representing mine cells.
 */
function revealMines() {
 for (let i = 0; i < rows; i++) {
 for (let j = 0; j < columns; j++) {
 if (boardArray[i][j] === "mine") {
 const MINE_CELL = BOARD.rows[i].cells[j];
 MINE_CELL.classList.add("mine");
 }
 }
 }
}
/**
 * Returns the CSS class name for a given number.
 *
 * @param {number} number - The number of adjacent mines.
 * @returns {string} The CSS class name for the number.
 */
function getNumberClass(number) {
 switch (number) {
 case 1:
 return "one";
 case 2:
 return "two";
 case 3:
 return "three";
 case 4:
 return "four";
 case 5:
 return "five";
 case 6:
 return "six";
 case 7:
 return "seven";
 case 8:
 return "eight";
 default:
 return "";
 }
}
/**
 * Changes the game level to the specified level.
 *
 * @param {string} level - The level to change to.
 */
function changeLevel(level) {
 if (currentLevel === level) return;
 gameFinish = false;
 LEVEL_BUTTONS[currentLevel].classList.remove("active");
 currentLevel = level;
 LEVEL_BUTTONS[currentLevel].classList.add("active");
 currentLevelConfig = LEVEL_SETTINGS[currentLevel];
 rows = currentLevelConfig.rows;
 columns = currentLevelConfig.cols;
 remainingMines = currentLevelConfig.mines;
 remainingFlags = remainingMines;
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 createBoard();
}
/**
 * Toggles the flag on a cell when the player right-clicks on it.
 *
 * @param {HTMLElement} cell - The HTML element representing the cell.
 */
function addFlagToCell(cell) {
 if (cell.classList.contains("zero") || cell.textContent || gameFinish) return;
 const HAS_FLAG = cell.classList.contains("flag");
 const ROW = cell.parentNode.rowIndex;
 const COL = cell.cellIndex;
 cell.classList.toggle("flag", !HAS_FLAG);
 remainingFlags += HAS_FLAG ? 1 : -1;
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 if (!HAS_FLAG && boardArray[ROW][COL] === "mine") correctFlagsCount++;
 if (checkWin()) {
 gameFinish = true;
 alert("You win!");
 return;
 }
}
/**
 * Checks if the player has won the game.
 * Returns true if all non-mine cells have been revealed and all flags are correctly placed on mine cells.
 *
 * @returns {boolean} True if the player has won, false otherwise.
 */
function checkWin() {
 return (
 totalCellsRevealed === rows * columns - remainingMines &&
 correctFlagsCount === remainingMines
 );
}
/**
 * Resets the game by resetting the game variables and creating a new board.
 */
function newGame() {
 gameFinish = false;
 correctFlagsCount = 0;
 totalCellsRevealed = 0;
 remainingMines = currentLevelConfig.mines;
 remainingFlags = remainingMines;
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 createBoard();
}
document.addEventListener("click", (event) => {
 const TARGET = event.target;
 if (TARGET.tagName === "TD") {
 const ROW = TARGET.parentNode.rowIndex;
 const COL = TARGET.cellIndex;
 revealCell(ROW, COL);
 } else if (TARGET === LEVEL_BUTTONS["beginner"]) {
 changeLevel("beginner");
 } else if (TARGET === LEVEL_BUTTONS["intermediate"]) {
 changeLevel("intermediate");
 } else if (TARGET === LEVEL_BUTTONS["advanced"]) {
 changeLevel("advanced");
 } else if (TARGET === NEW_GAME_BUTTON) {
 newGame();
 }
});
document.addEventListener("contextmenu", (event) => {
 const TARGET = event.target;
 if (TARGET.tagName === "TD") {
 event.preventDefault();
 addFlagToCell(TARGET);
 }
});
createBoard();

Github link
Live host

Source Link

Minesweeper game with HTML & CSS & JavaScript

I made a Minesweeper game using HTML, CSS and JavaScript and would like to ask for advice and feedback specifically on the code.
Here are some questions to review:

  • Is the use of HTML semantics correct? I would like to know if I am using HTML tags properly and if there is any way to improve the semantic structure of the code.
  • Is there any way to improve theorganization and reusability of the CSS code, any tips to make it more modular and easy to maintain?
  • Is there any way to optimize JavaScript code to make it more efficient and elegant? Any advice on best practices in terms of architecture or code writing?

Code:

const BOARD = document.getElementById("board");
const REMAINING_FLAGS_ELEMENT = document.getElementById("remaining-flags");
const NEW_GAME_BUTTON = document.getElementById("new-game");
const LEVEL_BUTTONS = {
 beginner: document.getElementById("beginner"),
 intermediate: document.getElementById("intermediate"),
 advanced: document.getElementById("advanced"),
};
const LEVEL_SETTINGS = {
 beginner: { rows: 9, cols: 9, mines: 10 },
 intermediate: { rows: 16, cols: 16, mines: 40 },
 advanced: { rows: 16, cols: 30, mines: 99 },
};
let currentLevel = "beginner";
let currentLevelConfig = LEVEL_SETTINGS[currentLevel];
let rows = currentLevelConfig.rows;
let columns = currentLevelConfig.cols;
let remainingMines = LEVEL_SETTINGS[currentLevel].mines;
let remainingFlags = remainingMines;
let totalCellsRevealed = 0;
let correctFlagsCount = 0;
let boardArray = [];
let gameFinish;
/**
 * Creates the game board by generating the HTML table structure.
 * Initializes the game board array.
 * Updates the remaining flags count displayed on the webpage.
 * Places the mines randomly on the board.
 * Counts the number of adjacent mines for each cell.
 */
function createBoard() {
 const BOARD_FRAGMENT = document.createDocumentFragment();
 BOARD.textContent = "";
 for (let i = 0; i < rows; i++) {
 const ROW = document.createElement("tr");
 boardArray[i] = [];
 for (let j = 0; j < columns; j++) {
 const CELL = document.createElement("td");
 boardArray[i][j] = 0;
 ROW.appendChild(CELL);
 }
 BOARD_FRAGMENT.appendChild(ROW);
 }
 BOARD.appendChild(BOARD_FRAGMENT);
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 placeMines();
 countAdjacentMines();
}
/**
 * Randomly places the mines on the game board.
 * Updates the "boardArray" with the "mine" value for each mine location.
 */
function placeMines() {
 let minesToPlace = remainingMines;
 while (minesToPlace > 0) {
 const RANDOM_ROW = Math.floor(Math.random() * rows);
 const RANDOM_COL = Math.floor(Math.random() * columns);
 if (boardArray[RANDOM_ROW][RANDOM_COL] !== "mine") {
 boardArray[RANDOM_ROW][RANDOM_COL] = "mine";
 minesToPlace--;
 }
 }
}
/**
 * Counts the number of adjacent mines for each non-mine cell on the game board.
 * Updates the "boardArray" with the corresponding mine count for each cell.
 */
function countAdjacentMines() {
 for (let row = 0; row < rows; row++) {
 for (let col = 0; col < columns; col++) {
 if (boardArray[row][col] !== "mine") {
 let minesCount = 0;
 for (let i = row - 1; i <= row + 1; i++) {
 for (let j = col - 1; j <= col + 1; j++) {
 const VALID_ROW = i >= 0 && i < rows;
 const VALID_COL = j >= 0 && j < columns;
 if (VALID_ROW && VALID_COL && boardArray[i][j] === "mine") {
 minesCount++;
 }
 }
 }
 boardArray[row][col] = minesCount;
 }
 }
 }
}
/**
 * Reveals the content of a cell and handles game logic.
 *
 * @param {number} row - The row index of the cell.
 * @param {number} col - The column index of the cell.
 */
function revealCell(row, col) {
 const CELL = BOARD.rows[row].cells[col];
 if (CELL.classList.contains("flag") || CELL.textContent || gameFinish) return;
 if (boardArray[row][col] === "mine") {
 gameFinish = true;
 revealMines();
 alert("Game over! You hit a mine.");
 } else if (boardArray[row][col] === 0) {
 revealAdjacentsCells(row, col);
 } else {
 const NUMBER_CLASS = getNumberClass(boardArray[row][col]);
 CELL.textContent = boardArray[row][col];
 CELL.classList.add(NUMBER_CLASS);
 }
 totalCellsRevealed++;
 if (checkWin()) {
 gameFinish = true;
 alert("You win!");
 return;
 }
}
/**
 * Reveals adjacents cells surrounding the specified cell.
 *
 * @param {number} row - The row index of the cell.
 * @param {number} col - The column index of the cell.
 */
function revealAdjacentsCells(row, col) {
 const CELL = BOARD.rows[row].cells[col];
 if (CELL.textContent) return;
 CELL.classList.add("zero");
 for (let i = row - 1; i <= row + 1; i++) {
 for (let j = col - 1; j <= col + 1; j++) {
 const VALID_ROW = i >= 0 && i < rows;
 const VALID_COL = j >= 0 && j < columns;
 if (VALID_ROW && VALID_COL && !(i === row && j === col)) {
 const CELL = BOARD.rows[i].cells[j];
 if (!CELL.classList.value) revealCell(i, j);
 }
 }
 }
}
/**
 * Reveals all the mines on the game board.
 * Adds the "mine" class to the HTML elements representing mine cells.
 */
function revealMines() {
 for (let i = 0; i < rows; i++) {
 for (let j = 0; j < columns; j++) {
 if (boardArray[i][j] === "mine") {
 const MINE_CELL = BOARD.rows[i].cells[j];
 MINE_CELL.classList.add("mine");
 }
 }
 }
}
/**
 * Returns the CSS class name for a given number.
 *
 * @param {number} number - The number of adjacent mines.
 * @returns {string} The CSS class name for the number.
 */
function getNumberClass(number) {
 switch (number) {
 case 1:
 return "one";
 case 2:
 return "two";
 case 3:
 return "three";
 case 4:
 return "four";
 case 5:
 return "five";
 case 6:
 return "six";
 case 7:
 return "seven";
 case 8:
 return "eight";
 default:
 return "";
 }
}
/**
 * Changes the game level to the specified level.
 *
 * @param {string} level - The level to change to.
 */
function changeLevel(level) {
 if (currentLevel === level) return;
 gameFinish = false;
 LEVEL_BUTTONS[currentLevel].classList.remove("active");
 currentLevel = level;
 LEVEL_BUTTONS[currentLevel].classList.add("active");
 currentLevelConfig = LEVEL_SETTINGS[currentLevel];
 rows = currentLevelConfig.rows;
 columns = currentLevelConfig.cols;
 remainingMines = currentLevelConfig.mines;
 remainingFlags = remainingMines;
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 createBoard();
}
/**
 * Toggles the flag on a cell when the player right-clicks on it.
 *
 * @param {HTMLElement} cell - The HTML element representing the cell.
 */
function addFlagToCell(cell) {
 if (cell.classList.contains("zero") || cell.textContent || gameFinish) return;
 const HAS_FLAG = cell.classList.contains("flag");
 const ROW = cell.parentNode.rowIndex;
 const COL = cell.cellIndex;
 cell.classList.toggle("flag", !HAS_FLAG);
 remainingFlags += HAS_FLAG ? 1 : -1;
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 if (!HAS_FLAG && boardArray[ROW][COL] === "mine") correctFlagsCount++;
 if (checkWin()) {
 gameFinish = true;
 alert("You win!");
 return;
 }
}
/**
 * Checks if the player has won the game.
 * Returns true if all non-mine cells have been revealed and all flags are correctly placed on mine cells.
 *
 * @returns {boolean} True if the player has won, false otherwise.
 */
function checkWin() {
 return (
 totalCellsRevealed === rows * columns - remainingMines &&
 correctFlagsCount === remainingMines
 );
}
/**
 * Resets the game by resetting the game variables and creating a new board.
 */
function newGame() {
 gameFinish = false;
 correctFlagsCount = 0;
 totalCellsRevealed = 0;
 remainingMines = currentLevelConfig.mines;
 remainingFlags = remainingMines;
 REMAINING_FLAGS_ELEMENT.textContent = remainingFlags;
 createBoard();
}
document.addEventListener("click", (event) => {
 const TARGET = event.target;
 if (TARGET.tagName === "TD") {
 const ROW = TARGET.parentNode.rowIndex;
 const COL = TARGET.cellIndex;
 revealCell(ROW, COL);
 } else if (TARGET === LEVEL_BUTTONS["beginner"]) {
 changeLevel("beginner");
 } else if (TARGET === LEVEL_BUTTONS["intermediate"]) {
 changeLevel("intermediate");
 } else if (TARGET === LEVEL_BUTTONS["advanced"]) {
 changeLevel("advanced");
 } else if (TARGET === NEW_GAME_BUTTON) {
 newGame();
 }
});
document.addEventListener("contextmenu", (event) => {
 const TARGET = event.target;
 if (TARGET.tagName === "TD") {
 event.preventDefault();
 addFlagToCell(TARGET);
 }
});
createBoard();
body {
 background-color: #55ddff;
 font-family: Arial, sans-serif;
}
h1 {
 text-align: center;
 color: #263be8;
 margin-bottom: 0;
}
.minesweeper {
 display: flex;
 flex-direction: column;
 justify-content: center;
 align-items: center;
 gap: 10px;
}
.btn {
 background-color: #0000ff;
 border: 0;
 color: #fff;
 cursor: pointer;
 font-weight: bold;
 line-height: normal;
 border-radius: 5px;
 padding: 5px;
 margin-left: 8px;
}
.active {
 background-color: red;
}
.info {
 color: red;
 font-weight: bold;
 font-size: 20px;
}
table {
 border-spacing: 0px;
}
td {
 padding: 0;
 width: 25px;
 height: 25px;
 background-color: #fff;
 border: 1px solid #a1a1a1;
 text-align: center;
 line-height: 20px;
 font-weight: bold;
 font-size: 18px;
}
.mine {
 background: #eeeeee url(../img/mine.png) no-repeat center;
 background-size: cover;
}
.flag {
 background: #eeeeee url(../img/flag.png) no-repeat center;
 background-size: cover;
}
.zero {
 background-color: #eeeeee;
}
.one {
 background-color: #eeeeee;
 color: #0332fe;
}
.two {
 background-color: #eeeeee;
 color: #019f02;
}
.three {
 background-color: #eeeeee;
 color: #ff2600;
}
.four {
 background-color: #eeeeee;
 color: #93208f;
}
.five {
 background-color: #eeeeee;
 color: #ff7f29;
}
.six {
 background-color: #eeeeee;
 color: #ff3fff;
}
.seven {
 background-color: #eeeeee;
 color: #53b8b4;
}
.eight {
 background-color: #eeeeee;
 color: #22ee0f;
}
 
 
<!DOCTYPE html>
<html lang="en">
 <head>
 <title>Minesweeper</title>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <link rel="stylesheet" href="assets/css/normalize.css">
 <link rel="stylesheet" href="assets/css/style.css">
 <link rel="icon" href="assets/img/favicon.png">
 <script src="assets/js/index.js" defer></script>
 </head>
 <body>
 <section class="minesweeper">
 <h1>Minesweeper</h1>
 <div class="level">
 <button class="btn active" id="beginner">Beginner</button>
 <button class="btn" id="intermediate">Intermediate</button>
 <button class="btn" id="advanced">Advanced</button>
 <button class="btn" id="new-game">New game</button>
 </div>
 <p class="info">🚩 <span id="remaining-flags">10</span></p>
 <table id="board"></table>
 </section>
 </body>
</html>
 

Github link

lang-css

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /