const difficulties = { 'easy': { 'size': [10, 10], 'mines': 10, }, 'medium': { 'size': [16, 16], 'mines': 40, }, 'hard': { 'size': [16, 30], 'mines': 99, }, }; const squareClassNames = { '.': 'unopened', 'B': 'unopened', 'F': 'unopened flag', 'BF': 'unopened flag', 'X': 'bomb', '0': 'opened empty', '1': 'opened one', '2': 'opened two', '3': 'opened three', '4': 'opened four', '5': 'opened five', '6': 'opened six', '7': 'opened seven', '8': 'opened eight', }; const squareTexts = { '.': '', 'B': '', 'F': '', 'BF': '', 'X': '', '0': '', '1': '1', '2': '2', '3': '3', '4': '4', '5': '5', '6': '6', '7': '7', '8': '8', }; const directions = [ [-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 0], [0, 1], [1, -1], [1, 0], [1, 1] ]; let board; let size; let mines; let flagCount; let timeInSeconds; let timerInterval; let unopenedCount; let bombRevealInterval; let squareListeners = {}; let isFirstClick = true; function initializeBoard() { board = []; unopenedCount = 0; for (let i = 0; i < size[0]; i++) { const row = []; for (let j = 0; j < size[1]; j++) { row.push('.'); unopenedCount += 1; } board.push(row); } } function generateMines(firstClickX, firstClickY) { let i = 0; while (i < mines) { const mineX = randomInt(0, size[0]); const mineY = randomInt(0, size[1]); console.log(mineX); console.log(mineY); if (!((mineX >= firstClickX - 1 && mineX <= firstClickX + 1 && mineY >= firstClickY - 1 && mineY <= firstClickY + 1) || board[mineX][mineY] === 'B')) { board[mineX][mineY] = 'B'; i++; unopenedCount -= 1; } } } function countMinesAroundSquare(row, column) { let mines = 0; for (const direction of directions) { const rowToCheck = row + direction[0]; const columnToCheck = column + direction[1]; if (rowToCheck < 0 || rowToCheck >= size[0] || columnToCheck < 0 || columnToCheck >= size[1]) { continue; } if (board[rowToCheck][columnToCheck].includes('B')) { mines += 1; } } return mines; } function stopGame(hasWon) { if (timerInterval) { clearInterval(timerInterval); } for (const square of document.getElementsByClassName('square')) { const listener = squareListeners[square.id]; square.removeEventListener('mousedown', listener); } squareListeners = {}; const gameResult = document.getElementById('result'); if (hasWon) { gameResult.innerText = 'You win!'; gameResult.className = 'win'; } else { gameResult.innerText = 'You lose.'; gameResult.className = 'defeat'; } document.getElementById('result-container').classList.remove('hide'); } function hasWon() { return unopenedCount === 0; } function revealSquare(row, column) { const mineCount = countMinesAroundSquare(row, column); if (board[row][column] == 'F') { flagCount += 1; renderFlagsCount(); } if (board[row][column] == '.') { unopenedCount -= 1; } board[row][column] = mineCount.toString(); const squareElement = document.getElementById(`${row}-${column}`); squareElement.innerHTML = squareTexts[board[row][column]]; squareElement.className = 'square ' + squareClassNames[board[row][column]]; if (hasWon()) { stopGame(true); } if (mineCount === 0) { for (const direction of directions) { const rowToOpen = row + direction[0]; const columnToOpen = column + direction[1]; if (rowToOpen < 0 || rowToOpen >= size[0] || columnToOpen < 0 || columnToOpen >= size[1]) { continue; } if (['.', 'F'].includes(board[rowToOpen][columnToOpen])) { revealSquare(rowToOpen, columnToOpen); } } } } function switchFlag(row, column) { const squareValue = board[row][column]; if (['.', 'B'].includes(squareValue)) { board[row][column] = squareValue === 'B' ? 'BF' : 'F'; flagCount -= 1; renderFlagsCount(); const squareElement = document.getElementById(`${row}-${column}`); squareElement.innerHTML = squareTexts[squareValue === 'B' ? 'BF' : 'F']; squareElement.className = 'square ' + squareClassNames[squareValue === 'BF' ? 'B' : '.']; } else if (['BF', 'F'].includes(squareValue)) { board[row][column] = squareValue === 'BF' ? 'B' : '.'; flagCount += 1; renderFlagsCount(); const squareElement = document.getElementById(`${row}-${column}`); squareElement.innerHTML = squareTexts[squareValue === 'BF' ? 'B' : '.']; squareElement.className = 'square ' + squareClassNames[squareValue === 'BF' ? 'B' : '.']; } } function loseGame() { const bombPositions = []; for (let i = 0; i < size[0]; i++) { for (let j = 0; j < size[1]; j++) { if (['B', 'BF'].includes(board[i][j])) { bombPositions.push(`${i}-${j}`); } } } bombRevealInterval = setInterval(() => { if (bombPositions.length == 0) { clearInterval(bombRevealInterval); } else { const squareElement = document.getElementById(bombPositions.shift()); squareElement.innerHTML = squareTexts['X']; squareElement.className = 'square ' + squareClassNames['X']; } }, 100); stopGame(false); } function configureSquareClickEvent(square) { const onClick = event => { const row = parseInt(square.id.split('-')[0]); const column = parseInt(square.id.split('-')[1]); if (event.which == 3) { switchFlag(row, column); } else { if (isFirstClick) { isFirstClick = false; generateMines(row, column); } const squareValue = board[row][column]; switch (squareValue) { case '.': revealSquare(row, column); break; case 'B': loseGame(); break; } } } squareListeners[square.id] = onClick; square.addEventListener('mousedown', onClick); } function renderBoard() { const boardElement = document.getElementById('board'); boardElement.innerHTML = ''; for (const row in board) { const rowElement = document.createElement('div'); rowElement.classList.add('row'); for (const column in board[row]) { const squareValue = board[row][column]; const squareElement = document.createElement('div'); squareElement.id = `${row}-${column}`; squareElement.className = `square ${squareClassNames[squareValue]}`; squareElement.innerHTML = squareTexts[squareValue]; configureSquareClickEvent(squareElement); rowElement.appendChild(squareElement); } boardElement.appendChild(rowElement); } boardElement.classList.remove('hide'); document.getElementById('board-header').classList.remove('hide'); } function renderFlagsCount() { const flagsCountContainerElement = document.getElementById('flags-count-container'); const flagsCountElement = document.getElementById('flags-count'); flagsCountElement.innerText = flagCount; flagsCountContainerElement.classList.remove('hide'); } function renderResultText() { const gameResult = document.getElementById('result'); gameResult.innerText = ''; document.getElementById('result-container').classList.add('hide'); } function startTimer() { timeInSeconds = 0; document.getElementById('timer-container').classList.remove('hide'); const timer = document.getElementById('timer'); timer.innerText = '00:00'; if (timerInterval) { clearInterval(timerInterval); } timerInterval = setInterval(function() { timeInSeconds += 1; const hours = Math.floor(timeInSeconds / 3600).toString(); const minutes = Math.floor((timeInSeconds % 3600) / 60).toString(); const seconds = (timeInSeconds % 60).toString(); let timerText = `${hours > 0 ? hours.padStart(2, '0') + ':' : ''}${minutes.padStart(2, '0')}:${seconds.padStart(2, '0')}`; timer.innerText = timerText; }, 1000); } function loadGame() { isFirstClick = true; initializeBoard(); renderBoard(); renderFlagsCount(); renderResultText(); startTimer(); } document.getElementById('start-game').addEventListener('click', function() { const difficulty = difficulties[document.getElementById('difficulty').value]; console.log(difficulty); size = difficulty['size']; mines = difficulty['mines']; flagCount = mines; if (bombRevealInterval) { clearInterval(bombRevealInterval); } loadGame(); }); document.getElementById('board').addEventListener('contextmenu', function(e) { e.preventDefault(); return false; }); function randomInt(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min)) + min; }