document.getElementById('startGame').addEventListener('click', function () { const gridSize = parseInt(document.getElementById('gridSize').value); const bombs = parseInt(document.getElementById('bombs').value); document.getElementById('settings').style.display = 'none'; document.getElementById('game').style.display = 'block'; renderGame(gridSize, bombs); }); const canvas = document.getElementById('game'); const ctx = canvas.getContext('2d'); class Minesweeper { constructor(width, height, bombs) { this.size = 25; this.field = new Array(width); this.bombs = new Array(width); for (let x = 0; x < this.field.length; x++) { this.field[x] = new Array(height); this.bombs[x] = new Array(height); for (let y = 0; y < this.field[x].length; y++) { this.field[x][y] = 1; this.bombs[x][y] = false; } } this.bombAmount = bombs > width * height / 2 ? width * height / 2 : bombs; this.width = width; this.height = height; this.firstClick = false; this.drawField(); } generateBombs() { for (let i = 0; i < this.bombAmount; i++) { let x = Math.floor(Math.random() * this.width); let y = Math.floor(Math.random() * this.height); (this.bombs[x][y] || this.field[x][y] == 0) ? i-- : this.bombs[x][y] = true; } this.firstClick = true; } getNearbyBombs(x, y) { let counter = 0; for (let newX = x - 1; newX <= x + 1; newX++) { for (let newY = y - 1; newY <= y + 1; newY++) { if (newX < this.field.length && newX >= 0 && newY < this.field[0].length && newY >= 0) { this.bombs[newX][newY] ? counter++ : {}; } } } return counter; } checkWin() { for (let x = 0; x < this.field.length; x++) { for (let y = 0; y < this.field[x].length; y++) { if (this.field[x][y] != 0 && !this.bombs[x][y]) { return; } else if (this.field[x][y] == 0 && this.bombs[x][y]) { alert(`[ERROR]: Square (${x}|${y}) should not be a bomb!`); this.bombs[x][y] = false; } } } this.drawField(); alert('You won!'); window.location.reload(); } markSquare(x, y) { if (x < this.field.length && y < this.field[0].length) { switch (this.field[x][y]) { case 1: this.field[x][y]++; break; case 2: this.field[x][y]++; break; case 3: this.field[x][y] = 1; break; default: break; } this.drawField(); } } uncoverSquare(x, y) { if (x < this.field.length && x >= 0 && y < this.field[0].length && y >= 0) { if (this.bombs[x][y] && this.field[x][y] == 1) { alert('You lost!'); window.location.reload(); } else if (this.field[x][y] == 1) { this.field[x][y] = 0; !this.firstClick ? this.generateBombs() : {}; if (this.getNearbyBombs(x, y) == 0) { for (let newX = x - 1; newX <= x + 1; newX++) { for (let newY = y - 1; newY <= y + 1; newY++) { if (newX < this.field.length && newX >= 0 && newY < this.field[0].length && newY >= 0) { this.field[newX][newY] == 1 ? this.uncoverSquare(newX, newY) : {}; } } } } }; this.checkWin(); this.drawField(); } } drawSquare(x, y, type) { ctx.lineWidth = 3; let uncovered = (x + y) % 2 === 0 ? '#D3D3D3' : '#A9A9A9'; let covered = (x + y) % 2 === 0 ? '#4CAF50' : '#81C784'; ctx.fillStyle = type != 0 ? covered : uncovered; ctx.fillRect(x * this.size, y * this.size, this.size, this.size); ctx.strokeStyle = '#000'; ctx.strokeRect(x * this.size, y * this.size, this.size, this.size); if (type != 1) { const fontSize = this.size / 2; const number = this.getNearbyBombs(x, y); let finalPrint; ctx.font = `${fontSize}px sans-serif`; ctx.fillStyle = '#000'; type == 0 ? finalPrint = number ? number : ' ' : {}; type == 2 ? finalPrint = '🚩' : {}; type == 3 ? finalPrint = '❓' : {}; ctx.fillText(finalPrint, x * this.size + fontSize / (type == 0 ? 1.5 : 1.8), y * this.size + fontSize * 1.3); } } drawField() { if (window.innerWidth > window.innerHeight) { this.size = window.innerHeight / (this.field[0].length + 4); } else { this.size = window.innerWidth / (this.field.length + 4); } canvas.width = this.size * this.field.length; canvas.height = this.size * this.field[0].length; const offsetX = (canvas.width - (this.field.length * this.size)) / 2; const offsetY = (canvas.height - (this.field[0].length * this.size)) / 2; for (let x = 0; x < this.field.length; x++) { for (let y = 0; y < this.field[x].length; y++) { this.drawSquare(x, y, this.field[x][y], offsetX, offsetY); } } } } const renderGame = (gridSize, bombs) => { let field = new Minesweeper(gridSize, gridSize, bombs); window.addEventListener('resize', () => field.drawField()); canvas.addEventListener('click', (event) => { const rect = canvas.getBoundingClientRect(); const x = Math.floor((event.clientX - rect.left) / field.size); const y = Math.floor((event.clientY - rect.top) / field.size); field.uncoverSquare(x, y); }); canvas.addEventListener('contextmenu', (event) => { event.preventDefault(); const rect = canvas.getBoundingClientRect(); const x = Math.floor((event.clientX - rect.left) / field.size); const y = Math.floor((event.clientY - rect.top) / field.size); field.markSquare(x, y); }); };