406 lines
8.9 KiB
JavaScript
406 lines
8.9 KiB
JavaScript
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();
|
|
}
|