const audio = document.getElementById("audio"); const audioVictory = document.getElementById("audioVictory"); const canvas = document.getElementById("tablero"); const ctx = canvas.getContext("2d"); const startButton = document.getElementById("startButton"); const banderas = document.getElementById("banderas"); const tiempo = document.getElementById("tiempo"); const FILAS = 25; const COLUMNAS = 25; const cellSize = 32; const nBombas = 60; const tilesGameOver = new Image(); tilesGameOver.src = "./img/GameOver.png"; const tilesVictory = new Image(); tilesVictory.src = "./img/Victory.png"; const tilesBorde = new Image(); tilesBorde.src = "./img/borde.png"; const tilesMisc = new Image(); tilesMisc.src = "./img/tiles.png"; let nBanderas = nBombas; let tiempoTranscurrido = 0; let showBombas = false; let cronometro; let tablero = []; let loopHandle; canvas.width = COLUMNAS * cellSize; canvas.height = FILAS * cellSize; function handleKey(event) { if (event.key === "1") { showBombas = !showBombas; } else if (event.key === "Escape") { gameOver(); } } function handleClick(event) { const { x, y } = getCellCoordinates(event); if (!tablero[y][x].tieneFlag) levantaCelda(y, x); } function handleContextMenu(event) { event.preventDefault(); const { x, y } = getCellCoordinates(event); marcaFlagCelda(y, x); } function getCellCoordinates(event) { const rect = canvas.getBoundingClientRect(); return { x: Math.floor((event.x - rect.left) / cellSize), y: Math.floor((event.y - rect.top) / cellSize), }; } function generaTablero() { for (let y = 0; y < FILAS; y++) { tablero[y] = []; for (let x = 0; x < COLUMNAS; x++) { tablero[y][x] = { esMina: false, abierto: false, tieneFlag: false, nAdyacentes: 0, }; } } for (let z = 0; z < nBombas; z++) { let x, y; do { y = Math.floor(Math.random() * FILAS); x = Math.floor(Math.random() * COLUMNAS); } while (tablero[y][x].esMina); tablero[y][x].esMina = true; for (let i = -1; i <= 1; i++) { for (let j = -1; j <= 1; j++) { if (y + i >= 0 && y + i < FILAS && x + j >= 0 && x + j < COLUMNAS) { if (!tablero[y + i][x + j].esMina) { tablero[y + i][x + j].nAdyacentes += 1; } } } } } } function levantaCelda(y, x) { if (!tablero[y][x].abierto) { tablero[y][x].abierto = true; if (tablero[y][x].tieneFlag) { tablero[y][x].tieneFlag = false; nBanderas++; } if (tablero[y][x].nAdyacentes === 0 && !tablero[y][x].esMina) { for (let i = -1; i <= 1; i++) { for (let j = -1; j <= 1; j++) { if ( y + i >= 0 && y + i < FILAS && x + j >= 0 && x + j < COLUMNAS && !(i === 0 && j === 0) ) { levantaCelda(y + i, x + j); } } } } if (tablero[y][x].esMina) { audio.play(); ctx.drawImage( tilesMisc, 128, 0, cellSize, cellSize, x * cellSize, y * cellSize, cellSize, cellSize ); gameOver(); } else if (comprobarVictoria()) { victoria(); } } } function comprobarVictoria() { return tablero.every((row) => row.every((cell) => cell.abierto || cell.esMina) ); } function marcaFlagCelda(y, x) { if (!tablero[y][x].abierto) { if (tablero[y][x].tieneFlag) { tablero[y][x].tieneFlag = false; nBanderas++; } else if (nBanderas > 0) { tablero[y][x].tieneFlag = true; nBanderas--; } } } function dibujaTablero() { for (let y = 0; y < FILAS; y++) { for (let x = 0; x < COLUMNAS; x++) { if (!tablero[y][x].abierto) { ctx.drawImage( tilesMisc, 32, 0, cellSize, cellSize, x * cellSize, y * cellSize, cellSize, cellSize ); } else { ctx.drawImage( tilesMisc, 96, 0, cellSize, cellSize, x * cellSize, y * cellSize, cellSize, cellSize ); if (tablero[y][x].nAdyacentes > 0) { ctx.font = "24px Bungee Spice"; //ctx.fillStyle = "black"; const text = tablero[y][x].nAdyacentes; const textX = x * cellSize + cellSize / 2 - ctx.measureText(text).width / 2; const textY = y * cellSize + cellSize / 2 + 10; ctx.fillText(text, textX, textY); } } if (showBombas && tablero[y][x].esMina) { ctx.drawImage( tilesMisc, 64, 0, cellSize, cellSize, x * cellSize, y * cellSize, cellSize, cellSize ); } if (tablero[y][x].tieneFlag) { ctx.drawImage( tilesMisc, 0, 0, cellSize, cellSize, x * cellSize, y * cellSize, cellSize, cellSize ); } } } dibujaBordesMapa(); } function dibujaBordesMapa() { for (let y = 1; y < FILAS - 1; y++) { ctx.drawImage( tilesBorde, 96, 0, cellSize, cellSize, 0, y * cellSize, cellSize, cellSize ); ctx.drawImage( tilesBorde, 96, 32, cellSize, cellSize, (COLUMNAS - 1) * cellSize, y * cellSize, cellSize, cellSize ); } for (let x = 1; x < COLUMNAS - 1; x++) { ctx.drawImage( tilesBorde, 64, 0, cellSize, cellSize, x * cellSize, 0, cellSize, cellSize ); ctx.drawImage( tilesBorde, 64, 32, cellSize, cellSize, x * cellSize, (FILAS - 1) * cellSize, cellSize, cellSize ); } ctx.drawImage(tilesBorde, 0, 0, cellSize, cellSize, 0, 0, cellSize, cellSize); ctx.drawImage( tilesBorde, 32, 0, cellSize, cellSize, (FILAS - 1) * cellSize, 0, cellSize, cellSize ); ctx.drawImage( tilesBorde, 0, 32, cellSize, cellSize, 0, (COLUMNAS - 1) * cellSize, cellSize, cellSize ); ctx.drawImage( tilesBorde, 32, 32, cellSize, cellSize, (FILAS - 1) * cellSize, (COLUMNAS - 1) * cellSize, cellSize, cellSize ); } function gameLoop() { tiempo.textContent = convertirTiempo(tiempoTranscurrido); banderas.textContent = nBanderas; dibujaTablero(); loopHandle = window.requestAnimationFrame(gameLoop); } function iniciarCronometro() { tiempoTranscurrido = 0; cronometro = setInterval(() => { tiempoTranscurrido++; }, 1000); } function detenerCronometro() { clearInterval(cronometro); } function convertirTiempo(tiempoSegundos) { const horas = Math.floor(tiempoSegundos / 3600); const minutos = Math.floor((tiempoSegundos % 3600) / 60); const segundos = tiempoSegundos % 60; const horasStr = String(horas).padStart(2, "0"); const minutosStr = String(minutos).padStart(2, "0"); const segundosStr = String(segundos).padStart(2, "0"); return `${horasStr}:${minutosStr}:${segundosStr}`; } function iniciarPartida() { startButton.blur(); startButton.style.visibility = "hidden"; nBanderas = nBombas; showBombas = false; canvas.addEventListener("click", handleClick); canvas.addEventListener("contextmenu", handleContextMenu); document.addEventListener("keydown", handleKey); generaTablero(); iniciarCronometro(); gameLoop(); } function terminarPartida() { canvas.removeEventListener("click", handleClick); canvas.removeEventListener("contextmenu", handleContextMenu); document.removeEventListener("keydown", handleKey); window.cancelAnimationFrame(loopHandle); detenerCronometro(); startButton.style.visibility = "visible"; startButton.focus(); } function victoria() { audioVictory.play(); const tamX = canvas.width / 1.2; const tamY = canvas.height / 1.2; const offsetX = (canvas.width - tamX) / 2; const offsetY = (canvas.height - tamY) / 2; ctx.drawImage(tilesVictory, offsetX, offsetY, tamX, tamY); terminarPartida(); } function gameOver() { for (let y = 0; y < FILAS; y++) { for (let x = 0; x < COLUMNAS; x++) { if (!tablero[y][x].abierto && tablero[y][x].esMina) { ctx.drawImage( tilesMisc, 96, 0, cellSize, cellSize, x * cellSize, y * cellSize, cellSize, cellSize ); ctx.drawImage( tilesMisc, 64, 0, cellSize, cellSize, x * cellSize, y * cellSize, cellSize, cellSize ); } } } dibujaBordesMapa(); const tamX = canvas.width / 1.5; const tamY = canvas.height / 1.5; const offsetX = (canvas.width - tamX) / 2; const offsetY = (canvas.height - tamY) / 2; ctx.drawImage(tilesGameOver, offsetX, offsetY, tamX, tamY); terminarPartida(); }